def getManifest(self): if self._v_manifest is None: xml = self.getFileFromContentPackage('imsmanifest.xml') if xml: self._v_manifest = IMSManifest(xml) return self._v_manifest
def getManifest(self): if self._v_manifest is None: xml = self.getFileFromContentPackage("imsmanifest.xml") if xml: self._v_manifest = IMSManifest(xml) return self._v_manifest
class SCO(Item): implements(ISCO, ITTWLockable, INameFromTitle) portal_type = "SCO" title = u"" description = u"" filename = u"" #track = None def __init__(self, id=None): super(SCO, self).__init__(id) # annotations = IAnnotations(self) # try: # TRACK_KEY = self.UID() # print 'TRACK_KEY',TRACK_KEY # except: # TRACK_KEY = 'eduintelligent.sco.track' # self.track = annotations.setdefault(TRACK_KEY, TrackingStorage()) self.track = TrackingStorage() # @property # def track(self): # annotations = IAnnotations(self) # TRACK_KEY = self.UID() # print 'TRACK_KEY',TRACK_KEY # return annotations.setdefault(TRACK_KEY, TrackingStorage()) def getUrlContents(self): """ """ scoId = "/".join(self.getPhysicalPath()) return EXTERNAL_URL + scoId def storePathSCO(self): """ """ scoId = "/".join(self.getPhysicalPath()) path = os.path.join(CONTENT_STORE, scoId.lstrip('/')) return path def protectDirs(self): """ :> robots.txt User-agent: * # aplicable a todos Disallow: / # impide la indexacion de todas las paginas ################################## :> .htaccess chmod 644 .htaccess IndexIgnore * """ def walker(directory): for name in os.listdir(directory): path = os.path.join(directory, name) if os.path.isdir(path): f = open(os.path.join(path, 'robots.txt'), 'w') f.write("""User-agent: *\nDisallow: / """) f.close() f = open(os.path.join(path, '.htaccess'), 'w') f.write("""IndexIgnore *\n""") f.close() walker(path) os.chmod(os.path.join(path, 'robots.txt'), 0644) os.chmod(os.path.join(path, '.htaccess'), 0644) scoId = "/".join(self.getPhysicalPath()) path = os.path.join(CONTENT_STORE, scoId.lstrip('/')) walker(path) def uploadContentPackage(self): """ this is an event after create or edit """ specificPath = self.storePathSCO() print "Ruta en donde se almacena: ", specificPath if hasattr(self.filename, 'data'): if os.path.exists(specificPath): # we want to replace an existing directory: utilities.removeDirectory(specificPath) utilities.createDirectory(specificPath) utilities.unzip().extract(StringIO(str(self.filename.data)), specificPath) self.protectDirs() ### create files to protect the public files self._v_manifest = None # invalidate manifest after upload self._v_itemCount = None self.filename = None def getFileFromContentPackage(self, subpath, doStream=False): path = os.path.join(self.storePathSCO(), subpath) if not os.path.exists(path): print "cuidado, no existe el archivo!!" return '' f = open(path) return f.read() # IMS and SCORM stuff: _v_manifest = None _v_itemCount = None def getManifest(self): if self._v_manifest is None: xml = self.getFileFromContentPackage('imsmanifest.xml') if xml: self._v_manifest = IMSManifest(xml) return self._v_manifest def getReportData(self): result = [] manifest = self.getManifest() # student is the student belonging to studentId or #the currently loggend-in user: #student = wbt.getStudent(self.studentId) #studentName = student and student.Title() or None if manifest: for org in self.getOrganizations(): orgTitle = self.getObjTitle(org) showOrgTitle = True for item in manifest.getSubItems(org): itemId = manifest.getIdentifier(item) itemIder = manifest.getItemIdentifier(itemId) row = { 'orgTitle': showOrgTitle and orgTitle or '', 'itemId': itemId, 'itemTitle': manifest.getTitle(item), 'itemLevel': manifest.getLevel(org, item), 'isStartable': itemIder, 'startResource': manifest.getStartResource(itemIder), #'student': student, } showOrgTitle = False # only show on first line result.append(row) return result ############################## # IMS Manifest Methods ############################## def getOrganizations(self): """ at same time update the variable volatile self._v_manifest """ manifest = self.getManifest() return manifest.getOrganizations() def getObjTitle(self, obj): return self._v_manifest and self._v_manifest.getTitle(obj) or '' def getItemCount(self): return self._v_manifest and self._v_manifest.getItemCount() or 0 def getItemTitle(self, item=0): return self._v_manifest and self._v_manifest.getItemTitle(item) or '' def getMasteryScore(self, item=0): return self._v_manifest and self._v_manifest.getMasteryScore( item) or '' def getLaunchData(self, item=0): return self._v_manifest and self._v_manifest.getLaunchData(item) or '' def getItemIndex(self, itemId): return self._v_manifest and self._v_manifest.getItemIndex(itemId) def getItems(self, documentNode): return self._v_manifest and self._v_manifest.getItems(documentNode) def getTitle(self, nodeElem): return self._v_manifest and self._v_manifest.getTitle(nodeElem) def getIdentifier(self, nodeElem): return self._v_manifest and self._v_manifest.getIdentifier(nodeElem) def getIdentifierRef(self, nodeElem): return self._v_manifest and self._v_manifest.getIdentifierRef(nodeElem) ####################### # SCORM Stuff ####################### scormElements = { 'cmi.core._children': 'student_id,student_name,lesson_location,credit,' 'lesson_status,entry,score,total_time,lesson_mode,exit,' 'session_time', 'cmi.core.score._children': 'raw,min,max', #replace with current data: 'cmi.core.student_name': 'Unknown', 'cmi.core.student_id': 'unknown', 'cmi.core.credit': 'credit', # should depend on lesson_mode 'cmi.core.lesson_mode': 'normal', #take from manifest: #'cmi.student_data.mastery_score': '', 'cmi.launch_data': '', #replace with actuall data from lmca: 'cmi.core.lesson_location': '', 'cmi.core.lesson_status': 'not attempted', 'cmi.core.entry': 'ab-initio', 'cmi.core.score.raw': '0', 'cmi.core.score.min': '', 'cmi.core.score.max': '', 'cmi.core.total_time': '0000:00:00.00', 'cmi.core.session_time': '0000:00:00.00', 'cmi.core.exit': '', 'cmi.suspend_data': '', 'cmi.comments': '', 'cmi.comments_from_lms': '', } def getScormData(self, memberId=None, item=None): studentName, studentId = self.getAuthenticatedNameAndId(memberId) manifest = self.getManifest() elements = self.scormElements.copy() elements['cmi.core.student_id'] = studentId elements['cmi.core.student_name'] = studentName elements['cmi.launch_data'] = manifest.getLaunchData(item) # content must not set mastery score mastery = manifest.getMasteryScore(item) if (mastery != 'not found' and mastery != 'Item not found'): elements['cmi.student_data.mastery_score'] = mastery try: trackData = self.getLastUserTrack(item, 0, studentId) print "trackData", trackData elements.update(trackData['data']) except: print "\n\n\n################### el usuario", studentId, "no tiene registros" return elements def saveToUserTrack(self, data, memberId=None, item=None): """ Store data (a mapping) in the user's scorm track. """ studentName, studentId = self.getAuthenticatedNameAndId(memberId) if studentId is None: return {} # setting lesson status self.setLessonStatus(data, self.getScormData(memberId, item)) data['cmi.core.entry'] = 'resume' data['cmi.core.total_time'] = addScormTime( data['cmi.core.total_time'], data['cmi.core.session_time']) self.recordTrack(item, 0, studentId, data) def setLessonStatus(self, data, scormData): status = data.get('cmi.core.lesson_status', None) entry = scormData.get('cmi.core.entry', 'resume') if status is None \ and scormData.get('cmi.core.lesson_mode', 'normal') == 'browse': status = 'browsed' if status is None: score = self._scormData.get('cmi.core.score.raw', '') masteryScore = scormData['cmi.student_data.mastery_score'] if masteryScore != '' and score != '' and int(score) >= int( masteryScore): status = 'passed' if status is None: if scormData.get('self.cmi.core.exit', '') == 'suspend': status = 'incomplete' # not yet supported by 21LL AK: #elif masteryScore != '' and score != '' and int(score) < int(masteryScore): # status = 'failed' else: status = 'browsed' data['cmi.core.lesson_status'] = status if status in ('complete', 'passed', 'failed'): entry = '' else: entry = 'resume' # maybe the best solution: #entry = '' #entry = 'ab-initio' data['cmi.core.entry'] = entry return data def startRun(self, assessmentId): """ """ return self.track.startRun(assessmentId) def stopRun(self, assessmentId): """ """ return self.track.stopRun(assessmentId) # the"classical" way of storing a track def recordTrack(self, assessmentId, runId, userName, data): """ """ print "recordTrack", self.track print "recordTrack(data)", data self.track.saveUserTrack(assessmentId, int(runId), userName, data) return runId or self.track.currentRuns[assessmentId] # SCORM-conformant access. Note that the data given (element names # and values) must conform to the SCORM data model. def scormSetValue(self, assessmentId, runId, userName, element, value): """ """ self.scormAPI.init(assessmentId, int(runId), userName) return self.scormAPI.setValue(element, value) def scormSetValues(self, assessmentId, runId, userName, mapping): """ """ self.scormAPI.init(assessmentId, int(runId), userName) return self.scormAPI.setValues(mapping) def scormGetValue(self, assessmentId, runId, userName, element): """ """ self.scormAPI.init(assessmentId, int(runId), userName) return self.scormAPI.getValue(element) # query methods def getTaskIds(self): """ """ return list(self.track.getTaskIds()) def getUserNames(self, taskId): """ """ return self.track.getUserNames(taskId) def listTracks(self, assessmentId, userName): """ """ criteria = {} if assessmentId: criteria['taskId'] = assessmentId if userName: criteria['userName'] = userName tracks = self.track.query(**criteria) return [self.trackToDict(t) for t in tracks] def getLastUserTrack(self, assessmentIdId, runId, userName): """ """ track = self.track.getLastUserTrack(assessmentIdId, int(runId), userName) return self.trackToDict(track) def query(self, criteria): """ """ if 'assessmentId' in criteria: criteria['taskId'] = criteria['assessmentId'] del criteria['assessmentId'] tracks = self.track.query(**criteria) return [self.trackToDict(t) for t in tracks] def trackToDict(self, track): result = track.metadata result['timeStamp'] = timeStamp2ISO(result['timeStamp']) result['assessmentId'] = result['taskId'] result['data'] = track.data and dict(track.data) or {} return result ###################################### # User Authentication ###################################### def getAuthenticatedNameAndId(self, memberId=None): """ """ memberName = "??????" portal_membership = getToolByName(self, 'portal_membership') if memberId: member = portal_membership.getMemberById(memberId) else: member = portal_membership.getAuthenticatedMember() if member is not None: memberId = member.getId() memberName = member.getProperty('fullname') or memberName memberName = memberName.split(' ')[0] return memberName, memberId
class SCO(Item): implements(ISCO, ITTWLockable, INameFromTitle) portal_type = "SCO" title = u"" description = u"" filename = u"" # track = None def __init__(self, id=None): super(SCO, self).__init__(id) # annotations = IAnnotations(self) # try: # TRACK_KEY = self.UID() # print 'TRACK_KEY',TRACK_KEY # except: # TRACK_KEY = 'eduintelligent.sco.track' # self.track = annotations.setdefault(TRACK_KEY, TrackingStorage()) self.track = TrackingStorage() # @property # def track(self): # annotations = IAnnotations(self) # TRACK_KEY = self.UID() # print 'TRACK_KEY',TRACK_KEY # return annotations.setdefault(TRACK_KEY, TrackingStorage()) def getUrlContents(self): """ """ scoId = "/".join(self.getPhysicalPath()) return EXTERNA_URL + scoId def storePathSCO(self): """ """ scoId = "/".join(self.getPhysicalPath()) path = os.path.join(SCORM_STORE, scoId.lstrip("/")) return path def protectDirs(self): """ :> robots.txt User-agent: * # aplicable a todos Disallow: / # impide la indexacion de todas las paginas ################################## :> .htaccess chmod 644 .htaccess IndexIgnore * """ def walker(directory): for name in os.listdir(directory): path = os.path.join(directory, name) if os.path.isdir(path): f = open(os.path.join(path, "robots.txt"), "w") f.write( """User-agent: *\nDisallow: / """ ) f.close() f = open(os.path.join(path, ".htaccess"), "w") f.write("""IndexIgnore *\n""") f.close() walker(path) os.chmod(os.path.join(path, "robots.txt"), 0644) os.chmod(os.path.join(path, ".htaccess"), 0644) scoId = "/".join(self.getPhysicalPath()) path = os.path.join(SCORM_STORE, scoId.lstrip("/")) walker(path) def uploadContentPackage(self): """ this is an event after create or edit """ specificPath = self.storePathSCO() print "Ruta en donde se almacena: ", specificPath if hasattr(self.filename, "data"): if os.path.exists(specificPath): # we want to replace an existing directory: utilities.removeDirectory(specificPath) utilities.createDirectory(specificPath) utilities.unzip().extract(StringIO(str(self.filename.data)), specificPath) self.protectDirs() ### create files to protect the public files self._v_manifest = None # invalidate manifest after upload self._v_itemCount = None self.filename = None def getFileFromContentPackage(self, subpath, doStream=False): path = os.path.join(self.storePathSCO(), subpath) if not os.path.exists(path): print "cuidado, no existe el archivo!!" return "" f = open(path) return f.read() # IMS and SCORM stuff: _v_manifest = None _v_itemCount = None def getManifest(self): if self._v_manifest is None: xml = self.getFileFromContentPackage("imsmanifest.xml") if xml: self._v_manifest = IMSManifest(xml) return self._v_manifest def getReportData(self): result = [] manifest = self.getManifest() # student is the student belonging to studentId or the currently loggend-in user: # student = wbt.getStudent(self.studentId) # studentName = student and student.Title() or None if manifest: for org in self.getOrganizations(): orgTitle = self.getObjTitle(org) showOrgTitle = True for item in manifest.getSubItems(org): itemId = manifest.getIdentifier(item) itemIder = manifest.getItemIdentifier(itemId) row = { "orgTitle": showOrgTitle and orgTitle or "", "itemId": itemId, "itemTitle": manifest.getTitle(item), "itemLevel": manifest.getLevel(org, item), "isStartable": itemIder, "startResource": manifest.getStartResource(itemIder), #'student': student, } showOrgTitle = False # only show on first line result.append(row) return result ############################## # IMS Manifest Methods ############################## def getOrganizations(self): """ at same time update the variable volatile self._v_manifest """ manifest = self.getManifest() return manifest.getOrganizations() def getObjTitle(self, obj): return self._v_manifest and self._v_manifest.getTitle(obj) or "" def getItemCount(self): return self._v_manifest and self._v_manifest.getItemCount() or 0 def getItemTitle(self, item=0): return self._v_manifest and self._v_manifest.getItemTitle(item) or "" def getMasteryScore(self, item=0): return self._v_manifest and self._v_manifest.getMasteryScore(item) or "" def getLaunchData(self, item=0): return self._v_manifest and self._v_manifest.getLaunchData(item) or "" def getItemIndex(self, itemId): return self._v_manifest and self._v_manifest.getItemIndex(itemId) def getItems(self, documentNode): return self._v_manifest and self._v_manifest.getItems(documentNode) def getTitle(self, nodeElem): return self._v_manifest and self._v_manifest.getTitle(nodeElem) def getIdentifier(self, nodeElem): return self._v_manifest and self._v_manifest.getIdentifier(nodeElem) def getIdentifierRef(self, nodeElem): return self._v_manifest and self._v_manifest.getIdentifierRef(nodeElem) ####################### # SCORM Stuff ####################### scormElements = { "cmi.core._children": "student_id,student_name,lesson_location,credit," "lesson_status,entry,score,total_time,lesson_mode,exit,session_time", "cmi.core.score._children": "raw,min,max", # replace with current data: "cmi.core.student_name": "Unknown", "cmi.core.student_id": "unknown", "cmi.core.credit": "credit", # should depend on lesson_mode "cmi.core.lesson_mode": "normal", # take from manifest: #'cmi.student_data.mastery_score': '', "cmi.launch_data": "", # replace with actuall data from lmca: "cmi.core.lesson_location": "", "cmi.core.lesson_status": "not attempted", "cmi.core.entry": "ab-initio", "cmi.core.score.raw": "0", "cmi.core.score.min": "", "cmi.core.score.max": "", "cmi.core.total_time": "0000:00:00.00", "cmi.core.session_time": "0000:00:00.00", "cmi.core.exit": "", "cmi.suspend_data": "", "cmi.comments": "", "cmi.comments_from_lms": "", } def getScormData(self, memberId=None, item=None): studentName, studentId = self.getAuthenticatedNameAndId(memberId) manifest = self.getManifest() elements = self.scormElements.copy() elements["cmi.core.student_id"] = studentId elements["cmi.core.student_name"] = studentName elements["cmi.launch_data"] = manifest.getLaunchData(item) # content must not set mastery score mastery = manifest.getMasteryScore(item) if mastery != "not found" and mastery != "Item not found": elements["cmi.student_data.mastery_score"] = mastery try: trackData = self.getLastUserTrack(item, 0, studentId) print "trackData", trackData elements.update(trackData["data"]) except: print "\n\n\n################### el usuario", studentId, "no tiene registros" return elements def saveToUserTrack(self, data, memberId=None, item=None): """ Store data (a mapping) in the user's scorm track. """ studentName, studentId = self.getAuthenticatedNameAndId(memberId) if studentId is None: return {} # setting lesson status self.setLessonStatus(data, self.getScormData(memberId, item)) data["cmi.core.entry"] = "resume" data["cmi.core.total_time"] = addScormTime(data["cmi.core.total_time"], data["cmi.core.session_time"]) self.recordTrack(item, 0, studentId, data) def setLessonStatus(self, data, scormData): status = data.get("cmi.core.lesson_status", None) entry = scormData.get("cmi.core.entry", "resume") if status is None and scormData.get("cmi.core.lesson_mode", "normal") == "browse": status = "browsed" if status is None: score = self._scormData.get("cmi.core.score.raw", "") masteryScore = scormData["cmi.student_data.mastery_score"] if masteryScore != "" and score != "" and int(score) >= int(masteryScore): status = "passed" if status is None: if scormData.get("self.cmi.core.exit", "") == "suspend": status = "incomplete" # not yet supported by 21LL AK: # elif masteryScore != '' and score != '' and int(score) < int(masteryScore): # status = 'failed' else: status = "browsed" data["cmi.core.lesson_status"] = status if status in ("complete", "passed", "failed"): entry = "" else: entry = "resume" # maybe the best solution: # entry = '' # entry = 'ab-initio' data["cmi.core.entry"] = entry return data def startRun(self, assessmentId): """ """ return self.track.startRun(assessmentId) def stopRun(self, assessmentId): """ """ return self.track.stopRun(assessmentId) # the"classical" way of storing a track def recordTrack(self, assessmentId, runId, userName, data): """ """ print "recordTrack", self.track print "recordTrack(data)", data self.track.saveUserTrack(assessmentId, int(runId), userName, data) return runId or self.track.currentRuns[assessmentId] # SCORM-conformant access. Note that the data given (element names # and values) must conform to the SCORM data model. def scormSetValue(self, assessmentId, runId, userName, element, value): """ """ self.scormAPI.init(assessmentId, int(runId), userName) return self.scormAPI.setValue(element, value) def scormSetValues(self, assessmentId, runId, userName, mapping): """ """ self.scormAPI.init(assessmentId, int(runId), userName) return self.scormAPI.setValues(mapping) def scormGetValue(self, assessmentId, runId, userName, element): """ """ self.scormAPI.init(assessmentId, int(runId), userName) return self.scormAPI.getValue(element) # query methods def getTaskIds(self): """ """ return list(self.track.getTaskIds()) def getUserNames(self, taskId): """ """ return self.track.getUserNames(taskId) def listTracks(self, assessmentId, userName): """ """ criteria = {} if assessmentId: criteria["taskId"] = assessmentId if userName: criteria["userName"] = userName tracks = self.track.query(**criteria) return [self.trackToDict(t) for t in tracks] def getLastUserTrack(self, assessmentIdId, runId, userName): """ """ track = self.track.getLastUserTrack(assessmentIdId, int(runId), userName) return self.trackToDict(track) def query(self, criteria): """ """ if "assessmentId" in criteria: criteria["taskId"] = criteria["assessmentId"] del criteria["assessmentId"] tracks = self.track.query(**criteria) return [self.trackToDict(t) for t in tracks] def trackToDict(self, track): result = track.metadata result["timeStamp"] = timeStamp2ISO(result["timeStamp"]) result["assessmentId"] = result["taskId"] result["data"] = track.data and dict(track.data) or {} return result ###################################### # User Authentication ###################################### def getAuthenticatedNameAndId(self, memberId=None): """ """ memberName = "??????" portal_membership = getToolByName(self, "portal_membership") if memberId: member = portal_membership.getMemberById(memberId) else: member = portal_membership.getAuthenticatedMember() if member is not None: memberId = member.getId() memberName = member.getProperty("fullname") or memberName memberName = memberName.split(" ")[0] return memberName, memberId