def __init__(self, objecttable, objectid, relatedUserID): if type(objectid) is not long: raise moodleObjects.moodleWarning("Invalid ObjectID provided") self.standardInit() # Get additional information required for this query="""SELECT * FROM %sassign c WHERE c.id = '%s'"""%(config.MOODLE_PREFIX, objectid) self.cursor.execute(query) row = self.cursor.fetchone() # if 'name' not in row: raise moodleObjects.moodleWarning("Failed to match record from " + objecttable + " with objectid " + str(objectid)) if( row is None ): self.setRemovedObject(objecttable, objectid, None) else: self.data = row self.data['type'] = self.TYPE_EN self.data['url'] = config.BASE_URI + "/mod/assign/view.php?id=" + str(objectid) self.groupings.append({ "id": row['url'], "definition": { "name": tincan.LanguageMap( {'en': row['name']} ), "type": self.TYPE, "description": tincan.LanguageMap( {'en': row['intro']} ), "extensions": { "http://lrs.learninglocker.net/define/extensions/moodle_module": row } } }) # Get the result query="""SELECT (gg.rawgrade/gg.rawgrademax) as scaled, gg.rawgrade as raw, gg.rawgrademax as max, true as completion, gg.feedback as response FROM %(prefix)sassign_grades ag JOIN %(prefix)sgrade_items gi ON gi.iteminstance = ag.assignment JOIN %(prefix)sgrade_grades gg ON gg.itemid = gi.id WHERE gi.itemmodule = 'assign' AND ag.id = '%(oid)s' AND gg.userid = '%(ruid)s'"""%{'prefix': config.MOODLE_PREFIX, 'oid': objectid, 'ruid': relatedUserID} self.cursor.execute(query) row = self.cursor.fetchone() # Quick fix for a empty response (regex criticals) if row['response'] is None: row['response'] = '' for key in ['scaled', 'raw', 'max']: if row[key] is not None: row[key] = int(row[key]) self.result = { "score": { "scaled": row['scaled'], "raw": row['raw'], "max": row['max'] }, "completion": row['completion'], "response": re.sub(r'[^\x00-\x7F]+',' ', row['response']) # Non-unicode characters, cannot be serialized so this replaces them with a space }
def __init__(self, objecttable, objectid, contextInstanceID): # This should NOT be called from a child. # Each child should have their own init function specific to that class self.standardInit() # Just a small safeguard against invalid data if type(objectid) is not long: raise moodleObjects.moodleWarning("Invalid ObjectID provided") # Setup the types that are normally const self.TYPE = self.VIEWED_TYPE_BASE + objecttable self.TYPE_EN = objecttable # Query the data # This may cause an error for a non-standard storage method query = """SELECT * FROM %s%s WHERE id = '%s'""" % (config.MOODLE_PREFIX, objecttable, objectid) self.cursor.execute(query) row = self.cursor.fetchone() if (row is None or len(row) == 0): raise moodleObjects.moodleError( "Failed to query information from " + config.MOODLE_PREFIX + objecttable) self.data = row self.data['type'] = self.TYPE_EN self.data[ 'url'] = config.BASE_URI + "/mod/" + objecttable + "/view.php?id=" + str( contextInstanceID)
def __init__(self, objecttable, objectid, contextInstanceID): # This should NOT be called from a child. # Each child should have their own init function specific to that class self.standardInit() # Just a small safeguard against invalid data if type(objectid) is not long: raise moodleObjects.moodleWarning("Invalid ObjectID provided") # Setup the types that are normally const self.TYPE = self.VIEWED_TYPE_BASE + objecttable self.TYPE_EN = objecttable # Query the data # This may cause an error for a non-standard storage method query="""SELECT * FROM %s%s WHERE id = '%s'"""%(config.MOODLE_PREFIX, objecttable, objectid) self.cursor.execute(query) row = self.cursor.fetchone() if (row is None or len(row) == 0): raise moodleObjects.moodleError("Failed to query information from " + config.MOODLE_PREFIX + objecttable) self.data = row self.data['type'] = self.TYPE_EN self.data['url'] = config.BASE_URI + "/mod/" + objecttable + "/view.php?id=" + str(contextInstanceID)
def __init__(self, courseID): if type(courseID) is not long: raise moodleObjects.moodleWarning("Invalid CourseID provided") self.standardInit() # Get additional information required for this query="""SELECT * FROM %scourse WHERE id = '%s';"""%(config.MOODLE_PREFIX, str(courseID)) self.cursor.execute(query) row = self.cursor.fetchone()
def __init__(self, objecttable, objectid, contextInstanceID): if type(objectid) is not long: raise moodleObjects.moodleWarning("Invalid ObjectID provided") self.standardInit() # Get additional information required for this query = """SELECT * FROM %sdialogue_conversations c WHERE c.id = '%s'""" % (config.MOODLE_PREFIX, objectid) self.cursor.execute(query) row = self.cursor.fetchone() if (row is None): self.setRemovedObject(objecttable, objectid, contextInstanceID) else: self.data = row self.data['type'] = self.TYPE_EN self.data[ 'url'] = config.BASE_URI + "/mod/dialogue/conversation.php?id=%s&conversationid=%s" % ( str(contextInstanceID), str(objectid)) self.data['name'] = row['subject'] self.data['intro'] = "A Moodle Conversation" # Build the additional grouping query = """SELECT p.* FROM %(prefix)sdialogue_conversations c JOIN %(prefix)sdialogue p ON c.dialogueid = p.id WHERE c.id = '%(oid)s';""" % { 'prefix': config.MOODLE_PREFIX, 'oid': objectid } self.cursor.execute(query) row = self.cursor.fetchone() row['url'] = config.BASE_URI + "/mod/dialogue/view.php?id=%s" % ( str(contextInstanceID)) row['type'] = self.TYPE row['intro'] = "A Moodle Dialogue" self.groupings.append({ "id": row['url'], "definition": { "name": tincan.LanguageMap({'en': row['name']}), "type": self.TYPE, "description": tincan.LanguageMap({'en': row['intro']}), "extensions": { "http://lrs.learninglocker.net/define/extensions/moodle_module": row } } })
def __init__(self, objecttable, objectid, contextInstanceID): if type(objectid) is not long: raise moodleObjects.moodleWarning("Invalid ObjectID provided") self.standardInit() # Get additional information required for this query = """SELECT * FROM %sforum_discussions c WHERE c.id = '%s'""" % (config.MOODLE_PREFIX, objectid) self.cursor.execute(query) row = self.cursor.fetchone() # if 'name' not in row: raise moodleObjects.moodleWarning("Failed to match record from " + objecttable + " with objectid " + str(objectid)) if (row is None): self.setRemovedObject(objecttable, objectid, contextInstanceID) else: self.data = row self.data['type'] = self.TYPE_EN self.data[ 'url'] = config.BASE_URI + "/mod/forum/discuss.php?id=" + str( objectid) self.data['intro'] = "A Moodle discussion." # Build the additional grouping query = """SELECT f.* FROM %sforum f WHERE f.id = '%s';""" % (config.MOODLE_PREFIX, self.data['forum']) self.cursor.execute(query) row = self.cursor.fetchone() row['url'] = config.BASE_URI + "/mod/forum/view.php?id=" + str( contextInstanceID) row['type'] = 'forum' self.groupings.append({ "id": row['url'], "definition": { "name": tincan.LanguageMap({'en': row['name']}), "type": "http://lrs.learninglocker.net/define/type/moodle/forum", "description": tincan.LanguageMap({'en': row['intro']}), "extensions": { "http://lrs.learninglocker.net/define/extensions/moodle_module": row } } })
def __init__(self, objecttable, objectid, contextInstanceID): if type(objectid) is not long: raise moodleObjects.moodleWarning("Invalid ObjectID provided") self.standardInit() # Get additional information required for this query="""SELECT * FROM %sforum_discussions c WHERE c.id = '%s'"""%(config.MOODLE_PREFIX, objectid) self.cursor.execute(query) row = self.cursor.fetchone() # if 'name' not in row: raise moodleObjects.moodleWarning("Failed to match record from " + objecttable + " with objectid " + str(objectid)) if( row is None ): self.setRemovedObject(objecttable, objectid, contextInstanceID) else: self.data = row self.data['type'] = self.TYPE_EN self.data['url'] = config.BASE_URI + "/mod/forum/discuss.php?id=" + str(objectid) self.data['intro'] = "A Moodle discussion." # Build the additional grouping query="""SELECT f.* FROM %sforum f WHERE f.id = '%s';"""%(config.MOODLE_PREFIX, self.data['forum']) self.cursor.execute(query) row = self.cursor.fetchone() row['url'] = config.BASE_URI + "/mod/forum/view.php?id=" + str(contextInstanceID) row['type'] = 'forum' self.groupings.append({ "id": row['url'], "definition": { "name": tincan.LanguageMap( {'en': row['name']} ), "type": "http://lrs.learninglocker.net/define/type/moodle/forum", "description": tincan.LanguageMap( {'en': row['intro']} ), "extensions": { "http://lrs.learninglocker.net/define/extensions/moodle_module": row } } })
def __init__(self, objecttable, objectid, contextInstanceID): if type(objectid) is not long: raise moodleObjects.moodleWarning("Invalid ObjectID provided") self.standardInit() # Get additiona information required for the object query="""SELECT a.* FROM %(prefix)sassign_submission s JOIN %(prefix)sassign a ON s.assignment = a.id WHERE s.id = '%(oid)s';"""%{'prefix': config.MOODLE_PREFIX, 'oid': objectid} self.cursor.execute(query) row = self.cursor.fetchone() if( row is None ): self.setRemovedObject(objecttable, objectid, contextInstanceID) else: self.data = row self.data['type'] = self.TYPE_EN self.data['url'] = config.BASE_URI + "/mod/assign/view.php?id=" + str(objectid)
def __init__(self, objecttable, objectid, contextInstanceID): if type(objectid) is not long: raise moodleObjects.moodleWarning("Invalid ObjectID provided") self.standardInit() # Get additional information required for this query="""SELECT * FROM %(prefix)sscorm_scoes ss JOIN %(prefix)sscorm s ON s.id = ss.scorm WHERE ss.id = '%(oid)s';"""%{'prefix': config.MOODLE_PREFIX, 'oid': objectid} self.cursor.execute(query) row = self.cursor.fetchone() if( row is None ): self.setRemovedObject(objecttable, objectid, contextInstanceID) else: row['type'] = self.TYPE_EN row['url'] = str(config.BASE_URI) + "/mod/scorm/view.php?id=" + str(contextInstanceID) self.data = row
def __init__(self, objecttable, objectid, contextInstanceID): if type(objectid) is not long: raise moodleObjects.moodleWarning("Invalid ObjectID provided") self.standardInit() # Get additional information required for this query="""SELECT * FROM %scourse c WHERE c.id = '%s'"""%(config.MOODLE_PREFIX, objectid) self.cursor.execute(query) row = self.cursor.fetchone() if( row is None ): self.setRemovedObject(objecttable, objectid, contextInstanceID) else: self.data = row self.data['type'] = self.TYPE_EN self.data['url'] = config.BASE_URI + "/course/view.php?id=" + str(objectid) self.data['name'] = row['fullname'] self.data['intro'] = row['summary']
def __init__(self, objecttable, objectid, contextInstanceID): if type(objectid) is not long: raise moodleObjects.moodleWarning("Invalid ObjectID provided") self.standardInit() # Get additional information required for this query = """SELECT * FROM %scourse c WHERE c.id = '%s'""" % (config.MOODLE_PREFIX, objectid) self.cursor.execute(query) row = self.cursor.fetchone() if (row is None): self.setRemovedObject(objecttable, objectid, contextInstanceID) else: self.data = row self.data['type'] = self.TYPE_EN self.data['url'] = config.BASE_URI + "/course/view.php?id=" + str( objectid) self.data['name'] = row['fullname'] self.data['intro'] = row['summary']
def __init__(self, objecttable, objectid, contextInstanceID): if type(objectid) is not long: raise moodleObjects.moodleWarning("Invalid ObjectID provided") self.standardInit() # Get additional information required for this query="""SELECT * FROM %squiz_attempts qa WHERE qa.id = '%s'"""%(config.MOODLE_PREFIX, objectid) self.cursor.execute(query) row = self.cursor.fetchone() # if 'name' not in row: raise moodleObjects.moodleWarning("Failed to match record from " + objecttable + " with objectid " + str(objectid)) if( row is None ): self.setRemovedObject(objecttable, objectid, contextInstanceID) else: self.data = row self.data['name'] = 'Attempt ' + str(objectid) self.data['type'] = self.TYPE_EN self.data['url'] = config.BASE_URI + "/mod/quiz/attempt.php?id=" + str(objectid) if 'sumgrades' in self.data and self.data['sumgrades'] is not None: self.data['sumgrades'] = int(row['sumgrades']) self.data['questions'] = {} # Add additional information to the object query="""SELECT qa.* FROM %squestion_attempts qa WHERE qa.questionusageid = '%s';"""%(config.MOODLE_PREFIX, row['uniqueid']) self.cursor.execute(query) questionAttempts = self.cursor.fetchall() # Yes this is bad. So is creating 3 connections to a db. for questionAttempt in questionAttempts: # Clean questionAttempt information questionAttempt['maxmark'] = int(questionAttempt['maxmark']) questionAttempt['maxfraction'] = int(questionAttempt['maxfraction']) questionAttempt['minfraction'] = int(questionAttempt['minfraction']) questionAttempt['steps'] = {} # Query the steps query="""SELECT * FROM %squestion_attempt_steps WHERE questionattemptid = '%s';"""%(config.MOODLE_PREFIX, questionAttempt['id']) self.cursor.execute(query) attemptSteps = self.cursor.fetchall() # Yes this is bad. So is creating 4 connections to a db. for step in attemptSteps: if 'fraction' in step and step['fraction'] is not None: step['fraction'] = int(step['fraction']) # Query the data query="""SELECT * FROM %squestion_attempt_step_data WHERE attemptstepid = '%s';"""%(config.MOODLE_PREFIX, step['id']) self.cursor.execute(query) stepData = self.cursor.fetchone() step['data'] = stepData questionAttempt['steps'][step['id']] = step self.data['questions'][questionAttempt['id']] = questionAttempt # Build the additional grouping query="""SELECT q.* FROM %squiz q WHERE q.id = '%s';"""%(config.MOODLE_PREFIX, self.data['quiz']) self.cursor.execute(query) row = self.cursor.fetchone() row['url'] = config.BASE_URI + "/mod/quiz/view.php?id=" + str(contextInstanceID) row['type'] = 'quiz' row['grade'] = int(row['grade']) row['sumgrades'] = int(row['sumgrades']) self.groupings.append({ "id": row['url'], "definition": { "name": tincan.LanguageMap( {'en': row['name']} ), "type": "http://lrs.learninglocker.net/define/type/moodle/quiz", "description": tincan.LanguageMap( {'en': row['intro']} ), "extensions": { "http://lrs.learninglocker.net/define/extensions/moodle_module": row } } })
def __init__(self, objecttable, objectid, relatedUserID): if type(objectid) is not long: raise moodleObjects.moodleWarning("Invalid ObjectID provided") self.standardInit() # Get additional information required for this query = """SELECT * FROM %sassign c WHERE c.id = '%s'""" % (config.MOODLE_PREFIX, objectid) self.cursor.execute(query) row = self.cursor.fetchone() # if 'name' not in row: raise moodleObjects.moodleWarning("Failed to match record from " + objecttable + " with objectid " + str(objectid)) if (row is None): self.setRemovedObject(objecttable, objectid, None) else: self.data = row self.data['type'] = self.TYPE_EN self.data[ 'url'] = config.BASE_URI + "/mod/assign/view.php?id=" + str( objectid) self.groupings.append({ "id": row['url'], "definition": { "name": tincan.LanguageMap({'en': row['name']}), "type": self.TYPE, "description": tincan.LanguageMap({'en': row['intro']}), "extensions": { "http://lrs.learninglocker.net/define/extensions/moodle_module": row } } }) # Get the result query = """SELECT (gg.rawgrade/gg.rawgrademax) as scaled, gg.rawgrade as raw, gg.rawgrademax as max, true as completion, gg.feedback as response FROM %(prefix)sassign_grades ag JOIN %(prefix)sgrade_items gi ON gi.iteminstance = ag.assignment JOIN %(prefix)sgrade_grades gg ON gg.itemid = gi.id WHERE gi.itemmodule = 'assign' AND ag.id = '%(oid)s' AND gg.userid = '%(ruid)s'""" % { 'prefix': config.MOODLE_PREFIX, 'oid': objectid, 'ruid': relatedUserID } self.cursor.execute(query) row = self.cursor.fetchone() # Quick fix for a empty response (regex criticals) if row['response'] is None: row['response'] = '' for key in ['scaled', 'raw', 'max']: if row[key] is not None: row[key] = int(row[key]) self.result = { "score": { "scaled": row['scaled'], "raw": row['raw'], "max": row['max'] }, "completion": row['completion'], "response": re.sub( r'[^\x00-\x7F]+', ' ', row['response'] ) # Non-unicode characters, cannot be serialized so this replaces them with a space }
def __init__(self, objecttable, objectid, contextInstanceID): if type(objectid) is not long: raise moodleObjects.moodleWarning("Invalid ObjectID provided") self.standardInit() # Get additional information required for this query = """SELECT * FROM %squiz_attempts qa WHERE qa.id = '%s'""" % (config.MOODLE_PREFIX, objectid) self.cursor.execute(query) row = self.cursor.fetchone() # if 'name' not in row: raise moodleObjects.moodleWarning("Failed to match record from " + objecttable + " with objectid " + str(objectid)) if (row is None): self.setRemovedObject(objecttable, objectid, contextInstanceID) else: self.data = row self.data['name'] = 'Attempt ' + str(objectid) self.data['type'] = self.TYPE_EN self.data[ 'url'] = config.BASE_URI + "/mod/quiz/attempt.php?id=" + str( objectid) if 'sumgrades' in self.data and self.data['sumgrades'] is not None: self.data['sumgrades'] = int(row['sumgrades']) self.data['questions'] = {} # Add additional information to the object query = """SELECT qa.* FROM %squestion_attempts qa WHERE qa.questionusageid = '%s';""" % (config.MOODLE_PREFIX, row['uniqueid']) self.cursor.execute(query) questionAttempts = self.cursor.fetchall( ) # Yes this is bad. So is creating 3 connections to a db. for questionAttempt in questionAttempts: # Clean questionAttempt information questionAttempt['maxmark'] = int(questionAttempt['maxmark']) questionAttempt['maxfraction'] = int( questionAttempt['maxfraction']) questionAttempt['minfraction'] = int( questionAttempt['minfraction']) questionAttempt['steps'] = {} # Query the steps query = """SELECT * FROM %squestion_attempt_steps WHERE questionattemptid = '%s';""" % (config.MOODLE_PREFIX, questionAttempt['id']) self.cursor.execute(query) attemptSteps = self.cursor.fetchall( ) # Yes this is bad. So is creating 4 connections to a db. for step in attemptSteps: if 'fraction' in step and step['fraction'] is not None: step['fraction'] = int(step['fraction']) # Query the data query = """SELECT * FROM %squestion_attempt_step_data WHERE attemptstepid = '%s';""" % (config.MOODLE_PREFIX, step['id']) self.cursor.execute(query) stepData = self.cursor.fetchone() step['data'] = stepData questionAttempt['steps'][step['id']] = step self.data['questions'][questionAttempt['id']] = questionAttempt # Build the additional grouping query = """SELECT q.* FROM %squiz q WHERE q.id = '%s';""" % (config.MOODLE_PREFIX, self.data['quiz']) self.cursor.execute(query) row = self.cursor.fetchone() row['url'] = config.BASE_URI + "/mod/quiz/view.php?id=" + str( contextInstanceID) row['type'] = 'quiz' row['grade'] = int(row['grade']) row['sumgrades'] = int(row['sumgrades']) self.groupings.append({ "id": row['url'], "definition": { "name": tincan.LanguageMap({'en': row['name']}), "type": "http://lrs.learninglocker.net/define/type/moodle/quiz", "description": tincan.LanguageMap({'en': row['intro']}), "extensions": { "http://lrs.learninglocker.net/define/extensions/moodle_module": row } } })
print "done (" + "{:,}".format(rowCount) + " records found)" # This actually gets the information in a cursor (stored on the database side due to memory overflow on client side) sys.stdout.write("Querying statements...") sys.stdout.flush() cur.execute("SELECT * FROM " + config.MOODLE_PREFIX + "logstore_standard_log WHERE timecreated > " + str(startTime) + " AND eventname NOT IN ('\\core\\event\\course_module_deleted', '\\core\\event\\calendar_event_created') ORDER BY timecreated ASC") state = time.time() # Parse the log cursor and handle each statement for row in cur: try: eventname=row['eventname'] # Check the eventname has a mapping if eventname not in ACTIVITY_ROUTES: raise moodleObjects.moodleWarning(eventname + " not mapped event.") continue # Actor is easy to build straight from a log row cur2.execute("SELECT Username, Firstname, Lastname FROM " + config.MOODLE_PREFIX + "user WHERE id = '" + str(row['userid']) + "';") usrInfo = cur2.fetchone() if( usrInfo is None ): raise moodleObjects.moodleWarning("Failed to find user information for userID: '%s'"%str(row['userid'])) actor = Agent( name = usrInfo['firstname'] + ' ' + usrInfo['lastname'], account = { 'name': usrInfo['username'], 'home_page': config.BASE_URI } )
sys.stdout.flush() cur.execute( "SELECT * FROM " + config.MOODLE_PREFIX + "logstore_standard_log WHERE timecreated > " + str(startTime) + " AND eventname NOT IN ('\\core\\event\\course_module_deleted', '\\core\\event\\calendar_event_created') ORDER BY timecreated ASC" ) state = time.time() # Parse the log cursor and handle each statement for row in cur: try: eventname = row['eventname'] # Check the eventname has a mapping if eventname not in ACTIVITY_ROUTES: raise moodleObjects.moodleWarning(eventname + " not mapped event.") continue # Actor is easy to build straight from a log row cur2.execute("SELECT Username, Firstname, Lastname FROM " + config.MOODLE_PREFIX + "user WHERE id = '" + str(row['userid']) + "';") usrInfo = cur2.fetchone() if (usrInfo is None): raise moodleObjects.moodleWarning( "Failed to find user information for userID: '%s'" % str(row['userid'])) actor = Agent(name=usrInfo['firstname'] + ' ' + usrInfo['lastname'], account={ 'name': usrInfo['username'],