From 96c2a6c60776913e7efcd1d36b4038848f712d72 Mon Sep 17 00:00:00 2001 From: Stuart Hallifax <stuart.hallifax@univ-lyon3.fr> Date: Mon, 18 Mar 2019 13:40:27 +0100 Subject: [PATCH] Added code base --- Context/ContextData.py | 3 + LogReader.py | 71 +++++++++++ ReadFileHandler.py | 17 +++ Traces/Action.py | 227 +++++++++++++++++++++++++++++++++ Traces/Operation.py | 177 +++++++++++++++++++++++++ Traces/OperationTransformer.py | 142 +++++++++++++++++++++ User/User.py | 58 +++++++++ User/UserProfileData.py | 51 ++++++++ ludimoodle_trace.log | 125 ++++++++++++++++++ 9 files changed, 871 insertions(+) create mode 100644 Context/ContextData.py create mode 100644 LogReader.py create mode 100644 ReadFileHandler.py create mode 100644 Traces/Action.py create mode 100644 Traces/Operation.py create mode 100644 Traces/OperationTransformer.py create mode 100644 User/User.py create mode 100644 User/UserProfileData.py create mode 100644 ludimoodle_trace.log diff --git a/Context/ContextData.py b/Context/ContextData.py new file mode 100644 index 0000000..5112edc --- /dev/null +++ b/Context/ContextData.py @@ -0,0 +1,3 @@ +class ContextData: + def __init__(self): + print('Init for context data not yet implemented') \ No newline at end of file diff --git a/LogReader.py b/LogReader.py new file mode 100644 index 0000000..2d20f7d --- /dev/null +++ b/LogReader.py @@ -0,0 +1,71 @@ +import csv +import pickle +import os +import time + +from User import User +from Traces.Operation import * +from Traces.OperationTransformer import transformOperation +from datetime import datetime + +if __name__ == "__main__" : + + maxFileAge = 600 # 600 seconds, 10 minutes + + needToCreateNewFile = True # not os.path.isfile('./users.pickle') + + if not needToCreateNewFile: + print("Backup found") + if time.time()-os.stat("./users.pickle").st_mtime < maxFileAge: + print("Loading from backup") + with open('users.pickle', 'rb') as backupfile: + users = pickle.load(backupfile) + else: + print("Backup too old") + needToCreateNewFile = True + + if needToCreateNewFile: + print("Creating new file") + unknowns = 0 + users = {} + with open("ludimoodle_trace.log", 'r') as file: + csvReader = csv.reader(file, delimiter=",")# delimiter = ";" + + for row in csvReader: + tmp = User.User(row[1].strip(), row[2]) + + if tmp.name not in users: + users[tmp.name] = tmp + else: + users[tmp.name].addGameElement(row[2]) + + user = users[tmp.name] + + op = buildOperation(row, user) + + if type(op) != Operation: + user.addOperation(op) + + # if type(op) == Operation: + # op.addOpType(row[3]) + # unknowns += 1 + + with open('users.pickle', 'wb') as backupfile: + pickle.dump(users, backupfile, protocol=pickle.HIGHEST_PROTOCOL) + + print("Created new user timeline") + + + selectedUser : User = users["score.stuart@univ-lyon3.fr"] + + todayTimeline = selectedUser.operationsPerformed + + #for timestamp in sorted(todayTimeline.keys()): + # print(todayTimeline[timestamp]) + + transformedTimeline = transformOperation(todayTimeline) + + selectedUser.actionsPerformed = transformedTimeline + + for action in transformedTimeline: + print(action) diff --git a/ReadFileHandler.py b/ReadFileHandler.py new file mode 100644 index 0000000..dc6bf73 --- /dev/null +++ b/ReadFileHandler.py @@ -0,0 +1,17 @@ +from watchdog.events import FileSystemEventHandler +import os + +class FileReaderHandler(FileSystemEventHandler): + + currentLine = 0 + + def on_modified(self, event): + print('File has been modified') + print(event.src_path) + + with open(event.src_path, 'rb') as f: + f.seek(-2, os.SEEK_END) + while f.read(1) != b'\n': + f.seek(-2, os.SEEK_CUR) + print(f.readline().decode()) + diff --git a/Traces/Action.py b/Traces/Action.py new file mode 100644 index 0000000..0f32925 --- /dev/null +++ b/Traces/Action.py @@ -0,0 +1,227 @@ +from datetime import datetime +from datetime import timedelta +from User import User +from typing import NewType +from Traces import Operation + +GameElement = NewType("GameElement", str) + +class Action: + def __init__(self, start: datetime, end: datetime, user: User, gameElement: GameElement): + """ + + :param start: A datetime at the start of the Action + :param end: A date time at the end of the Action + :param user: The user that performed the Action + :param gameElement: The game element that the user had when the performed the Action + """ + self.start = start + self.end = end + self.user = user + self.gameElement = gameElement + self.actionName = "Action" + + def __str__(self): + return str(self.actionName) + ";" + str(self.user.name) + ";" + str(self.gameElement) + ";" + str(self.start) + ";" + str(self.end) + ";" + + + def __lt__(self, other): + if self.start < other.start: + return True + elif self.start > other.start: + return False + else: + return self.end < other.end + +class Pause(Action): + def __init__(self, start: datetime, end: datetime, user: User, gameElement: GameElement, op1: Operation, op2: Operation): + """ + + :param start: A datetime at the start of the Action + :param end: A date time at the end of the Action + :param user: The user that performed the Action + :param gameElement: The game element that the user had when the performed the Action + :param op1: The first operation + :param op2: The second operation + """ + super().__init__(start, end, user, gameElement) + self.op1 = op1 + self.op2 = op2 + self.actionName = "Pause" + + def __str__(self): + return super().__str__() + "Duration:" + str(self.getPauseDuration())+ ";" + str(self.op1) + ";" + str(self.op2) + + def getPauseDuration(self) -> timedelta: + """ + Get the duration of the Pause Action + :return: The timedelta between the start and the end + """ + return self.end - self.start + +class AttemptQuiz(Action): + def __init__(self, start: datetime, end: datetime, user: User, gameElement: GameElement, quizId: str, attemptNumber: int): + """ + + :param start: A datetime at the start of the Action + :param end: A date time at the end of the Action + :param user: The user that performed the Action + :param gameElement: The game element that the user had when the performed the Action + :param quizId: The Id of the attempted quiz + :param attemptNumber: The attempt number + """ + super().__init__(start, end, user, gameElement) + self.quizId = quizId + self.attemptNumber = attemptNumber + self.actionName = "AttemptQuiz" + + def __str__(self): + return super().__str__() + "QuizId:"+ self.quizId + ";" + "Attempt:" + str(self.attemptNumber) + +class ResumeQuizAttempt(Action): + def __init__(self, start: datetime, end: datetime, user: User, gameElement: GameElement, quizId: str, attemptNumber: int): + """ + + :param start: A datetime at the start of the Action + :param end: A date time at the end of the Action + :param user: The user that performed the Action + :param gameElement: The game element that the user had when the performed the Action + :param quizId: The Id of the attempted quiz + :param attemptNumber: The attempt number + """ + super().__init__(start, end, user, gameElement) + self.quizId = quizId + self.attemptNumber = attemptNumber + self.actionName = "ResumeQuizAttempt" + + def __str__(self): + return super().__str__() + "QuizId:"+ self.quizId + ";" + "Attempt:" + str(self.attemptNumber) + +class CompleteQuestion(Action): + def __init__(self, start: datetime, end: datetime, user: User, gameElement: GameElement, success: bool = True): + """ + + :param start: A datetime at the start of the Action + :param end: A date time at the end of the Action + :param user: The user that performed the Action + :param gameElement: The game element that the user had when the performed the Action + :param success: Whether the question was answered correctly or not + """ + super().__init__(start, end, user, gameElement) + self.success = success + self.actionName = "CompleteQuestion" + + def __str__(self): + return super().__str__() + "Success:" + str(self.success) + +class CompleteQuiz(Action): + def __init__(self, start: datetime, end: datetime, user: User, gameElement: GameElement, quizId: str, attemptNumber: int, interruption: bool = False, success: bool = True): + + """ + + :param start: A datetime at the start of the Action + :param end: A date time at the end of the Action + :param user: The user that performed the Action + :param gameElement: The game element that the user had when the performed the Action + :param quizId: The Id of the attempted quiz + :param attemptNumber: The attempt number + :param interruption: Indicate whether the quiz was interrupted or not + :param success: Indicate whether the student validated the quiz or not + """ + super().__init__(start, end, user, gameElement) + self.quizId = quizId + self.attemptNumber = attemptNumber + self.interruption = interruption + self.success = success + self.actionName = "CompleteQuiz" + + + def __str__(self): + return super().__str__() + "QuizId:"+ self.quizId + ";" + "Attempt:" + str(self.attemptNumber) + ";Interruption:" + str(self.interruption) + ";Success:" + str(self.success) + +class RestartQuiz(Action): + def __init__(self, start: datetime, end: datetime, user: User, gameElement: GameElement, quizId: str, attemptNumber: int, afterSuccess: bool = False, + inCurrentLesson: bool = True, + interruptCurrentLesson: bool = False): + """ + + :param start: A datetime at the start of the Action + :param end: A date time at the end of the Action + :param user: The user that performed the Action + :param gameElement: The game element that the user had when the performed the Action + :param quizId: The Id of the attempted quiz + :param attemptNumber: The attempt number + :param afterSuccess: Indicate whether the quiz was restarted after a successful quiz or not + :param inCurrentLesson: Indicate whether the quiz was restarted during a later lesson + :param interruptCurrentLesson: Indicate whether the quiz was restarted before the current lesson was completed + """ + super().__init__(start, end, user, gameElement) + self.quizId = quizId + self.attemptNumber = attemptNumber + self.afterSuccess = afterSuccess + self.inCurrentLesson = inCurrentLesson + self.interruptCurrentLesson = interruptCurrentLesson + self.actionName = "RestartQuiz" + + def __str__(self): + return super().__str__() + "QuizId:"+ self.quizId + ";" + "Attempt:" + str(self.attemptNumber) + ";AfterSuccess:" + str(self.afterSuccess) + ";InCurrentLesson:" + str(self.inCurrentLesson) + ";InterruptCurrentLesson:" + str(self.interruptCurrentLesson) + +class AbandonQuiz(Action): + def __init__(self, start: datetime, end: datetime, user: User, gameElement: GameElement, quizId: str, + attemptNumber: int, numberOfQuestions: int, correctQuestions: int): + """ + + :param start: A datetime at the start of the Action + :param end: A date time at the end of the Action + :param user: The user that performed the Action + :param gameElement: The game element that the user had when the performed the Action + :param quizId: The Id of the attempted quiz + :param attemptNumber: The attempt number + :param numberOfQuestions: The number of attempted questions + :param correctQuestions: The number of correct questions + """ + super().__init__(start, end, user, gameElement) + self.quizId = quizId + self.attemptNumber = attemptNumber + self.numberOfQuestions = numberOfQuestions + self.correctQuestions = correctQuestions + self.actionName = "AbandonQuiz" + + def __str__(self): + return super().__str__() + "QuizId:" + self.quizId + ";Attempt:" + str(self.attemptNumber) + ";NumberOfQuestions:" + str(self.numberOfQuestions) + ";CorrectionQuestions" + str(self.correctQuestions) + +class StartLesson(Action): + def __init__(self, start: datetime, end: datetime, user: User, gameElement: GameElement, lessonId: str): + """ + + :param start: A datetime at the start of the Action + :param end: A date time at the end of the Action + :param user: The user that performed the Action + :param gameElement: The game element that the user had when the performed the Action + :param lessonId: The Id of the lesson started + """ + super().__init__(start, end, user, gameElement) + self.lessonId = lessonId + self.actionName = "StartLesson" + + def __str__(self): + return super().__str__() + "LessonId:"+ self.lessonId + +class CompleteLesson(Action): + def __init__(self, start: datetime, end: datetime, user: User, gameElement: GameElement, lessonId: str, success: bool = True): + """ + + :param start: A datetime at the start of the Action + :param end: A date time at the end of the Action + :param user: The user that performed the Action + :param gameElement: The game element that the user had when the performed the Action + :param lessonId: The Id of the lesson started + :param success: Has the lesson been completed fully or not + """ + super().__init__(start, end, user, gameElement) + self.lessonId = lessonId + self.success = success + self.actionName = "CompleteLesson" + + def __str__(self): + return super().__str__() + "LessonId:"+ self.lessonId + ";Success:" + str(self.success) \ No newline at end of file diff --git a/Traces/Operation.py b/Traces/Operation.py new file mode 100644 index 0000000..4b48ea0 --- /dev/null +++ b/Traces/Operation.py @@ -0,0 +1,177 @@ +from datetime import datetime + + +def buildOperation(logRow, user): + #Row structure + timestamp = logRow[0].strip() + gameElement = logRow[2].strip() + op = logRow[3].strip() + + if len(logRow) > 4: + parameters = logRow[4:] + + #General operations + if op == "login": + return Login(timestamp, user, gameElement) + elif op == "course_pageview" : + return CoursePageview(timestamp, user, gameElement, parameters[0]) + elif op == "feature_change" : #Not yet implemented + return FeatureChange(timestamp, user, gameElement) + elif op == "dashboard" : + return Dashboard(timestamp, user, gameElement) + + #Quiz related operations + elif op == "quiz_moduleview": + return QuizModuleview(timestamp, user, gameElement, parameters[1]) + elif op == "quiz_attempt_started" : + return QuizAttemptStarted(timestamp, user, gameElement, parameters[0], parameters[1], parameters[2]) + elif op == "quiz_attempt_finished" : + return QuizAttemptFinished(timestamp, user, gameElement, parameters[0], parameters[1], parameters[2]) + #elif op == "quiz_start": + # return QuizAttemptStarted(timestamp, user, gameElement, parameters[0], 1) + elif op == "quiz_pageview": + return QuizPageview(timestamp, user, gameElement, parameters[1], parameters[2], parameters[3]) + elif op == "question_gradedright": + return QuestionGradedRight(timestamp, user, gameElement, parameters[3], parameters[0], parameters[2]) + elif op == "question_gradedwrong": + return QuestionGradedWrong(timestamp, user, gameElement, parameters[3], parameters[0], parameters[2]) + elif op == "quiz_review": + return QuizReview(timestamp, user, gameElement, parameters[0], parameters[1]) + elif op == "quiz_submit": + return QuizSubmit(timestamp, user, gameElement, parameters[0], parameters[1]) + elif op == "quiz_results": #Not yet active + return QuizResults(timestamp, user, gameElement, parameters[0], parameters[1]) + else: + return Operation(timestamp, user, gameElement) + +class Operation: + def __init__(self, timestamp, user, gameElement): + self.timestamp = datetime.utcfromtimestamp(int(timestamp)) + self.user = user + self.gameElement = gameElement + + def print(self): + return "Simple Operation" + + def addOpType(self, opType): + self.opType = opType + + def __str__(self): + return "Unknown operation :" + self.timestamp.strftime('%Y-%m-%d %H:%M:%S') + " " + self.opType + + +class Login(Operation): + def __init__(self, timestamp, user, gameElement): + super().__init__(timestamp, user, gameElement) + + def __str__(self): + return "Login :" + self.timestamp.strftime('%Y-%m-%d %H:%M:%S') + +class Dashboard(Operation): + def __init__(self, timestamp, user, gameElement): + super().__init__(timestamp, user, gameElement) + + def __str__(self): + return "Dashboard :" + self.timestamp.strftime('%Y-%m-%d %H:%M:%S') + +class CoursePageview(Operation): + def __init__(self, timestamp, user, gameElement, page): + super().__init__(timestamp, user, gameElement) + self.page = page + + def __str__(self): + return "Pageview :" + self.timestamp.strftime('%Y-%m-%d %H:%M:%S') + " page : " + self.page + +class FeatureChange(Operation): + def __init__(self , timestamp, user, gameElement): + super().__init__(timestamp, user, gameElement) + + def __str__(self): + return "FeatureChange :" + self.timestamp.strftime('%Y-%m-%d %H:%M:%S') + +class QuizModuleview(Operation): + def __init__(self, timestamp, user, gameElement, quizId): + super().__init__(timestamp, user, gameElement) + self.quizId = quizId.strip() + + def __str__(self): + return "Quiz module view :" + self.timestamp.strftime('%Y-%m-%d %H:%M:%S') + self.quizId + +class QuizAttemptStarted(Operation): + def __init__(self, timestamp, user, gameElement, attemptNumber, course, quizId): + super().__init__(timestamp, user, gameElement) + self.quizId = quizId.strip() + self.attemptNumber = attemptNumber + self.course = course + + def __str__(self): + return "Quiz attempt started :" + self.timestamp.strftime('%Y-%m-%d %H:%M:%S') + self.quizId + str(self.attemptNumber) + +class QuizAttemptFinished(Operation): + def __init__(self, timestamp, user, gameElement, attemptNumber, course, quizId): + super().__init__(timestamp, user, gameElement) + self.quizId = quizId.strip() + self.attemptNumber = attemptNumber + self.course = course + + def __str__(self): + return "Quiz attempt finished:" + self.timestamp.strftime('%Y-%m-%d %H:%M:%S') + self.quizId + str(self.attemptNumber) + + +class QuizPageview(Operation): + def __init__(self, timestamp, user, gameElement, quizId, attemptNumber, pageNumber): + super().__init__(timestamp, user, gameElement) + self.quizId = quizId.strip() + self.attemptNumber = attemptNumber + self.pageNumber = pageNumber + + def __str__(self): + return "Quiz page view :" + self.timestamp.strftime('%Y-%m-%d %H:%M:%S') + self.quizId + self.attemptNumber + self.pageNumber + +class QuestionGradedRight(Operation): + def __init__(self, timestamp, user, gameElement, quizId, attemptNumber, pageNumber): + super().__init__(timestamp, user, gameElement) + self.quizId = quizId.strip() + self.attemptNumber = attemptNumber.strip() + self.pageNumber = pageNumber.strip() + + def __str__(self): + return "Question graded right :" + self.timestamp.strftime('%Y-%m-%d %H:%M:%S') + self.quizId + self.attemptNumber + self.pageNumber + +class QuestionGradedWrong(Operation): + def __init__(self, timestamp, user, gameElement, quizId, attemptNumber, pageNumber): + super().__init__(timestamp, user, gameElement) + self.quizId = quizId.strip() + self.attemptNumber = attemptNumber.strip() + self.pageNumber = pageNumber.strip() + + def __str__(self): + return "Question graded right :" + self.timestamp.strftime('%Y-%m-%d %H:%M:%S') + self.quizId + self.attemptNumber + self.pageNumber + +class QuizReview(Operation): + def __init__(self, timestamp, user, gameElement, quizId, attemptNumber): + super().__init__(timestamp, user, gameElement) + self.quizId = quizId + self.attemptNumber = attemptNumber + + def __str__(self): + return "Quiz review :" + self.timestamp.strftime('%Y-%m-%d %H:%M:%S') + self.quizId + self.attemptNumber + +class QuizSubmit(Operation): + def __init__(self, timestamp, user, gameElement, quizId, attemptNumber): + super().__init__(timestamp, user, gameElement) + self.quizId = quizId + self.attemptNumber = attemptNumber + + def __str__(self): + return "Quiz submit :" + self.timestamp.strftime('%Y-%m-%d %H:%M:%S') + self.quizId + self.attemptNumber + +class QuizResults(Operation): + def __init__(self, timestamp, user, gameElement, quizId, attemptNumber, quizResults): + super().__init__(timestamp, user, gameElement) + self.quizId = quizId + self.attemptNumber = attemptNumber + self.quizResults = quizResults + + def __str__(self): + return "Quiz results :" + self.timestamp.strftime('%Y-%m-%d %H:%M:%S') + self.quizId + self.attemptNumber + self.quizResults \ No newline at end of file diff --git a/Traces/OperationTransformer.py b/Traces/OperationTransformer.py new file mode 100644 index 0000000..48fd0f0 --- /dev/null +++ b/Traces/OperationTransformer.py @@ -0,0 +1,142 @@ +from Traces.Operation import * +from Traces.Action import * +from datetime import timedelta + +# STATIC VALUES +pauseDelta = timedelta(minutes=5) +passRate = 0.7 #The % of correct answers to consider that a quiz is successful + +def transformOperation(timeline): + + # The transformed timeline that contains the start timestamp and corresponding Action + transformedTimeline = [] + + # VARIABLES USED + attemptNumber = "" + quizStart = '' + inQuiz = False + interruptedQuiz = False + numberOfQuestions = 0.0 + correctQuestions = 0.0 + previousQuizzes = {} + success = '' + + + timestamps = sorted(timeline.keys()) + i = 0 + + + + while i< len(timestamps): + # Get current operation + op : Operation = timeline[timestamps[i]] + + if not inQuiz: + if type(op) in [QuizPageview]: + transformedTimeline.append(ResumeQuizAttempt(op.timestamp, op.timestamp, op.user, op.gameElement, op.quizId, op.attemptNumber)) + quizStart = op + inQuiz = True + interruptedQuiz = True + + try: + numberOfQuestions = previousQuizzes[quizStart.quizId][1] + correctQuestions = previousQuizzes[quizStart.quizId][2] + except KeyError: + print("No previous info about this past quiz") + numberOfQuestions = 0.0 + correctQuestions = 0.0 + success = "Unsure" + + previousQuizzes[quizStart.quizId] = [False, numberOfQuestions, correctQuestions] + + if i != 0: + previousOp = timeline[timestamps[i-1]] + + if inQuiz: + if type(op) not in [QuizPageview, QuestionGradedRight, QuestionGradedWrong, QuizAttemptFinished]: + interruptedQuiz = True + + if (type(op) is QuizPageview) and (op.quizId != quizStart.quizId): + transformedTimeline.append(AbandonQuiz(op.timestamp, op.timestamp, op.user, op.gameElement, quizStart.quizId, quizStart.attemptNumber, numberOfQuestions, correctQuestions)) + transformedTimeline.append(ResumeQuizAttempt(op.timestamp, op.timestamp, op.user, op.gameElement, op.quizId, op.attemptNumber)) + quizStart = op + inQuiz = True + interruptedQuiz = True + + try: + numberOfQuestions = previousQuizzes[quizStart.quizId][1] + correctQuestions = previousQuizzes[quizStart.quizId][2] + except KeyError: + print("No previous info about this past quiz") + numberOfQuestions = 0.0 + correctQuestions = 0.0 + success = "Unsure" + + previousQuizzes[quizStart.quizId] = [False, numberOfQuestions, correctQuestions] + + i+=1 + continue + + + # Pause check + if timestamps[i] - timestamps[i-1] >= pauseDelta: + transformedTimeline.append(Pause(timestamps[i-1], timestamps[i], op.user, op.gameElement, previousOp, op)) + i+=1 + continue + + # Attempt quiz check + if (type(op) is QuizAttemptStarted) and (type(previousOp) is QuizModuleview): + #if op.quizId == previousOp.quizId: #This check doesn't currently work as ids don't match ! + if inQuiz: + transformedTimeline.append(AbandonQuiz(previousOp.timestamp, op.timestamp, op.user, op.gameElement, quizStart.quizId, quizStart.attemptNumber, numberOfQuestions, correctQuestions)) + + if op.quizId not in previousQuizzes.keys(): + transformedTimeline.append(AttemptQuiz(previousOp.timestamp, op.timestamp, op.user, op.gameElement, op.quizId, op.attemptNumber)) + else: + transformedTimeline.append(RestartQuiz(previousOp.timestamp, op.timestamp, op.user, op.gameElement, op.quizId, op.attemptNumber, previousQuizzes[op.quizId])) + + quizStart = op + inQuiz = True + interruptedQuiz = False + numberOfQuestions = 0.0 + correctQuestions = 0.0 + success = False + + previousQuizzes[quizStart.quizId] = [False, numberOfQuestions, correctQuestions] + + + i+=1 + continue + + # Complete question check + if (type(op) in [QuestionGradedRight, QuestionGradedWrong]) and (type(previousOp) is QuizPageview): + if op.quizId == previousOp.quizId: + transformedTimeline.append(CompleteQuestion(previousOp.timestamp, op.timestamp, op.user, op.gameElement, type(op) is QuestionGradedRight)) + i+=1 + numberOfQuestions += 1 + + if type(op) is QuestionGradedRight: + correctQuestions += 1 + + previousQuizzes[quizStart.quizId][1] = numberOfQuestions + previousQuizzes[quizStart.quizId][2] = correctQuestions + continue + + #Complete quiz check + if type(op) is QuizAttemptFinished: + if success != "Unsure" : + success = correctQuestions/numberOfQuestions > passRate + + transformedTimeline.append(CompleteQuiz(quizStart.timestamp, op.timestamp, op.user, op.gameElement, op.quizId, op.attemptNumber, interruptedQuiz, success)) + inQuiz = False + interruptedQuiz = False + + previousQuizzes[op.quizId] = [success, numberOfQuestions, correctQuestions] + + numberOfQuestions = 0.0 + correctQuestions = 0.0 + + i+=1 + + + return transformedTimeline \ No newline at end of file diff --git a/User/User.py b/User/User.py new file mode 100644 index 0000000..342835b --- /dev/null +++ b/User/User.py @@ -0,0 +1,58 @@ +import User.UserProfileData as upd +from Traces.Action import * + +class User: + def __init__(self, name, gameElement, userProfileData = upd.UserProfileData(0,0,0,0)): + self.name = name + self.gameElements = [gameElement] + self.currentGameElement = gameElement + self.operationsPerformed = {} + self.actionsPerformed = {} + self.userProfile = userProfileData + + def addGameElement(self, gameElement): + if gameElement not in self.gameElements: + self.gameElements.append(gameElement) + + self.currentGameElement = gameElement + + def numberOfGameElements(self): + return len(self.gameElements) + + def addOperation(self, operation): + self.operationsPerformed[operation.timestamp] = operation + + def printTimeline(self): + for timestamp in sorted(self.operationsPerformed.keys()): + print(self.operationsPerformed[timestamp]) + + def getTimeline(self): + return self.operationsPerformed + + def getTimelineFromDate(self, date): + tmp = {} + for timestamp in self.operationsPerformed: + if timestamp.date() == date.date(): + tmp[timestamp] = self.operationsPerformed[timestamp] + + return tmp + + def getNumberOfPauses(self): + i = 0 + for timestamp in self.actionsPerformed : + if type(self.actionsPerformed[timestamp]) is Pause: + i+=1 + return i + + def exportActions(self): + output = "Action;User;GameElement;Debut;Fin;Infos\n" + + with open("transformedTrace.csv", 'w') as file: + for action in self.actionsPerformed: + output += str(action) + "\n" + + file.write(output) + + + def __eq__(self, other): + return self.name == other.name \ No newline at end of file diff --git a/User/UserProfileData.py b/User/UserProfileData.py new file mode 100644 index 0000000..3ace757 --- /dev/null +++ b/User/UserProfileData.py @@ -0,0 +1,51 @@ +class HexadScores: + @classmethod + def fromQuestionnaire(cls, questionnaireScoreList): + print("from questionnaireScores not yet implemented") + return HexadScores(0,0,0,0,0,0) + + def __init__(self, achieverScore, playerScore, socialiserScore, freeSpiritScore, disruptorScore, philanthropistScore): + self.achiever = achieverScore + self.player = playerScore + self.socialiser = socialiserScore + self.freeSpirit = freeSpiritScore + self.disruptor = disruptorScore + self.philanthropist = philanthropistScore + + +class MotivationScores: + @classmethod + def fromQuestionnaire(cls, questionnaireScoreList): + print("method not yet implemented") + return MotivationScores() + + # Types de motivation + # Intrinseque : + # MICO : a la connaissance + # MIAC : a l'accomplissement + # MIST : a la simulation + # Extrinseque : + # MEID : a la regulation identifiee + # MEIN : a la regulation introjectee + # MERE : a la regulation externe + # Amotivation : + # AMOT : amotivation + + def __init__(self, mico, miac, mist, meid, mein, mere, amot): + self.mico = mico + self.miac = miac + self.mist = mist + self.meid = meid + self.mein = mein + self.mere = mere + self.amot = amot + + def variation(self, motivationScores): + return MotivationScores(motivationScores.mico - self.mico, motivationScores.miac - self.miac, motivationScores.mist - self.mist, motivationScores.meid - self.meid, motivationScores.mein - self.mein, motivationScores.mere - self.mere, motivationScores.amot - self.amot) + +class UserProfileData: + def __init__(self, hexadScores, age, gender, initialMotivationScores): + self.hexadScores = hexadScores + self.age = age + self.gender = gender + self.initialMotivationScores = initialMotivationScores \ No newline at end of file diff --git a/ludimoodle_trace.log b/ludimoodle_trace.log new file mode 100644 index 0000000..55875ff --- /dev/null +++ b/ludimoodle_trace.log @@ -0,0 +1,125 @@ +1551973816, score.stuart@univ-lyon3.fr , score , login , username : score.stuart@univ-lyon3.fr,,,,,, +1551973817, score.stuart@univ-lyon3.fr , score , dashboard , ,,,,,, +1551973819, score.stuart@univ-lyon3.fr , score , course_pageview , course : calcul-litteral-2019,,,,,, +1551973821, score.stuart@univ-lyon3.fr , score , course_pageview , course : calcul-litteral-2019 , coursesectionnumber : 2,,,,, +1551973824, score.stuart@univ-lyon3.fr , score , quiz_moduleview , course : calcul-litteral-2019 , quiz : 138,,,,, +1551973826, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 1.1 : QCM , quiz_attempts : 1263 , section : 2,,, +1551973830, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 1.1 : QCM , quiz_attempts : 1263 , section : 2,,, +1551973830, score.stuart@univ-lyon3.fr , score , question_gradedright , attempt : 1 , course : calcul-litteral-2019 , question : 1 , quiz : Exercice 1.1 : QCM , section : 2 , sequence : 1 , state : gradedright +1551973831, score.stuart@univ-lyon3.fr , score , score_update , course : 206 , property : points , section : , value : 1000,,, +1551973834, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 1.1 : QCM , quiz_attempts : 1263 , section : 2,,, +1551973838, score.stuart@univ-lyon3.fr , score , question_gradedright , attempt : 1 , course : calcul-litteral-2019 , question : 2 , quiz : Exercice 1.1 : QCM , section : 2 , sequence : 1 , state : gradedright +1551973839, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 1.1 : QCM , quiz_attempts : 1263 , section : 2,,, +1551973839, score.stuart@univ-lyon3.fr , score , score_update , course : 206 , property : points , section : , value : 2000,,, +1551973843, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 1.1 : QCM , quiz_attempts : 1263 , section : 2,,, +1551973847, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 1.1 : QCM , quiz_attempts : 1263 , section : 2,,, +1551973847, score.stuart@univ-lyon3.fr , score , score_update , course : 206 , property : points , section : , value : 3000,,, +1551973847, score.stuart@univ-lyon3.fr , score , question_gradedright , attempt : 1 , course : calcul-litteral-2019 , question : 3 , quiz : Exercice 1.1 : QCM , section : 2 , sequence : 1 , state : gradedright +1551973850, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 1.1 : QCM , quiz_attempts : 1263 , section : 2,,, +1551973855, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 1.1 : QCM , quiz_attempts : 1263 , section : 2,,, +1551973855, score.stuart@univ-lyon3.fr , score , score_update , course : 206 , property : points , section : , value : 4000,,, +1551973855, score.stuart@univ-lyon3.fr , score , question_gradedright , attempt : 1 , course : calcul-litteral-2019 , question : 4 , quiz : Exercice 1.1 : QCM , section : 2 , sequence : 1 , state : gradedright +1551973859, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 1.1 : QCM , quiz_attempts : 1263 , section : 2,,, +1551973862, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 1.1 : QCM , quiz_attempts : 1263 , section : 2,,, +1551973862, score.stuart@univ-lyon3.fr , score , question_gradedright , attempt : 1 , course : calcul-litteral-2019 , question : 5 , quiz : Exercice 1.1 : QCM , section : 2 , sequence : 1 , state : gradedright +1551973863, score.stuart@univ-lyon3.fr , score , score_update , course : 206 , property : points , section : , value : 5000,,, +1551973866, score.stuart@univ-lyon3.fr , score , quiz_summaryview , course : calcul-litteral-2019 , quiz : Exercice 1.1 : QCM , quiz_attempts : 1263 , section : 2,,, +1551973868, score.stuart@univ-lyon3.fr , score , quiz_review , course : calcul-litteral-2019 , quiz : Exercice 1.1 : QCM , quiz_attempts : 1263 , section : 2,,, +1551973868, score.stuart@univ-lyon3.fr , score , quiz_submit , course : calcul-litteral-2019 , quiz : Exercice 1.1 : QCM , quiz_attempts : 1263 , section : 2,,, +1551973868, score.stuart@univ-lyon3.fr , score , quiz_attempt_finished , attempt : 1 , course : calcul-litteral-2019 , quiz : Exercice 1.1 : QCM , section : 2,,, +1551973874, score.stuart@univ-lyon3.fr , score , quiz_moduleview , course : calcul-litteral-2019 , quiz : 143,,,,, +1551973876, score.stuart@univ-lyon3.fr , score , quiz_start , course : calcul-litteral-2019 , quiz_attempts : 1267,,,,, +1551973876, score.stuart@univ-lyon3.fr , score , quiz_attempt_started , attempt : 1 , course : calcul-litteral-2019 , quiz : Exercice 1.2 : QCM , section : 2,,, +1551973877, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 1.2 : QCM , quiz_attempts : 1267 , section : 2,,, +1551973882, score.stuart@univ-lyon3.fr , score , question_gradedwrong , attempt : 1 , course : calcul-litteral-2019 , question : 1 , quiz : Exercice 1.2 : QCM , section : 2 , sequence : 1 , state : gradedwrong +1551973883, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 1.2 : QCM , quiz_attempts : 1267 , section : 2,,, +1551973886, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 1.2 : QCM , quiz_attempts : 1267 , section : 2,,, +1551973890, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 1.2 : QCM , quiz_attempts : 1267 , section : 2,,, +1551973890, score.stuart@univ-lyon3.fr , score , question_gradedwrong , attempt : 1 , course : calcul-litteral-2019 , question : 2 , quiz : Exercice 1.2 : QCM , section : 2 , sequence : 1 , state : gradedwrong +1551973893, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 1.2 : QCM , quiz_attempts : 1267 , section : 2,,, +1551973898, score.stuart@univ-lyon3.fr , score , question_gradedwrong , attempt : 1 , course : calcul-litteral-2019 , question : 3 , quiz : Exercice 1.2 : QCM , section : 2 , sequence : 1 , state : gradedwrong +1551973899, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 1.2 : QCM , quiz_attempts : 1267 , section : 2,,, +1551973902, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 1.2 : QCM , quiz_attempts : 1267 , section : 2,,, +1551973906, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 1.2 : QCM , quiz_attempts : 1267 , section : 2,,, +1551973906, score.stuart@univ-lyon3.fr , score , question_gradedwrong , attempt : 1 , course : calcul-litteral-2019 , question : 4 , quiz : Exercice 1.2 : QCM , section : 2 , sequence : 1 , state : gradedwrong +1551973908, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 1.2 : QCM , quiz_attempts : 1267 , section : 2,,, +1551973919, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 1.2 : QCM , quiz_attempts : 1267 , section : 2,,, +1551973919, score.stuart@univ-lyon3.fr , score , score_update , course : 211 , property : points , section : , value : 1000,,, +1551973919, score.stuart@univ-lyon3.fr , score , question_gradedright , attempt : 1 , course : calcul-litteral-2019 , question : 5 , quiz : Exercice 1.2 : QCM , section : 2 , sequence : 1 , state : gradedright +1551973922, score.stuart@univ-lyon3.fr , score , quiz_summaryview , course : calcul-litteral-2019 , quiz : Exercice 1.2 : QCM , quiz_attempts : 1267 , section : 2,,, +1551973924, score.stuart@univ-lyon3.fr , score , quiz_review , course : calcul-litteral-2019 , quiz : Exercice 1.2 : QCM , quiz_attempts : 1267 , section : 2,,, +1551973924, score.stuart@univ-lyon3.fr , score , quiz_submit , course : calcul-litteral-2019 , quiz : Exercice 1.2 : QCM , quiz_attempts : 1267 , section : 2,,, +1551973924, score.stuart@univ-lyon3.fr , score , quiz_attempt_finished , attempt : 1 , course : calcul-litteral-2019 , quiz : Exercice 1.2 : QCM , section : 2,,, +1551973929, score.stuart@univ-lyon3.fr , score , course_pageview , course : calcul-litteral-2019,,,,,, +1551973935, score.stuart@univ-lyon3.fr , score , course_pageview , course : calcul-litteral-2019 , coursesectionnumber : 2,,,,, +1551973935, score.stuart@univ-lyon3.fr , score , score_update , course : calcul-litteral-2019 , property : points , section : 2 , value : 11000,,, +1552399610, score.stuart@univ-lyon3.fr , score , login , username : score.stuart@univ-lyon3.fr,,,,,, +1552399611, score.stuart@univ-lyon3.fr , score , dashboard , ,,,,,, +1552399613, score.stuart@univ-lyon3.fr , score , course_pageview , course : calcul-litteral-2019,,,,,, +1552399613, score.stuart@univ-lyon3.fr , score , score_update , course : , property : points , section : , value : 22000,,, +1552399616, score.stuart@univ-lyon3.fr , score , course_pageview , course : calcul-litteral-2019 , coursesectionnumber : 2,,,,, +1552399619, score.stuart@univ-lyon3.fr , score , quiz_moduleview , course : calcul-litteral-2019 , quiz : 143,,,,, +1552399626, score.stuart@univ-lyon3.fr , score , course_pageview , course : calcul-litteral-2019 , coursesectionnumber : 2,,,,, +1552399628, score.stuart@univ-lyon3.fr , score , quiz_moduleview , course : calcul-litteral-2019 , quiz : 138,,,,, +1552399632, score.stuart@univ-lyon3.fr , score , course_pageview , course : calcul-litteral-2019,,,,,, +1552399634, score.stuart@univ-lyon3.fr , score , course_pageview , course : calcul-litteral-2019 , coursesectionnumber : 3,,,,, +1552399636, score.stuart@univ-lyon3.fr , score , quiz_moduleview , course : calcul-litteral-2019 , quiz : 146,,,,, +1552399638, score.stuart@univ-lyon3.fr , score , quiz_start , course : calcul-litteral-2019 , quiz_attempts : 1271,,,,, +1552399638, score.stuart@univ-lyon3.fr , score , quiz_attempt_started , attempt : 1 , course : calcul-litteral-2019 , quiz : Exercice 2.1 : simplification de produit , section : 3,,, +1552399639, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 2.1 : simplification de produit , quiz_attempts : 1271 , section : 3,,, +1552399644, score.stuart@univ-lyon3.fr , score , question_gradedright , attempt : 1 , course : calcul-litteral-2019 , question : 1 , quiz : Exercice 2.1 : simplification de produit , section : 3 ,, +1552399645, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 2.1 : simplification de produit , quiz_attempts : 1271 , section : 3,,, +1552399645, score.stuart@univ-lyon3.fr , score , score_update , course : 214 , property : points , section : , value : 1000,,, +1552399648, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 2.1 : simplification de produit , quiz_attempts : 1271 , section : 3,,, +1552399654, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 2.1 : simplification de produit , quiz_attempts : 1271 , section : 3,,, +1552399654, score.stuart@univ-lyon3.fr , score , score_update , course : 214 , property : points , section : , value : 2000,,, +1552399654, score.stuart@univ-lyon3.fr , score , question_gradedright , attempt : 1 , course : calcul-litteral-2019 , question : 2 , quiz : Exercice 2.1 : simplification de produit , section : 3 ,, +1552399657, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 2.1 : simplification de produit , quiz_attempts : 1271 , section : 3,,, +1552399668, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 2.1 : simplification de produit , quiz_attempts : 1271 , section : 3,,, +1552399668, score.stuart@univ-lyon3.fr , score , score_update , course : 214 , property : points , section : , value : 3000,,, +1552399668, score.stuart@univ-lyon3.fr , score , question_gradedright , attempt : 1 , course : calcul-litteral-2019 , question : 3 , quiz : Exercice 2.1 : simplification de produit , section : 3 ,, +1552399676, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 2.1 : simplification de produit , quiz_attempts : 1271 , section : 3,,, +1552399680, score.stuart@univ-lyon3.fr , score , course_pageview , course : calcul-litteral-2019,,,,,, +1552399682, score.stuart@univ-lyon3.fr , score , course_pageview , course : calcul-litteral-2019 , coursesectionnumber : 2,,,,, +1552399684, score.stuart@univ-lyon3.fr , score , quiz_moduleview , course : calcul-litteral-2019 , quiz : 143,,,,, +1552399686, score.stuart@univ-lyon3.fr , score , quiz_start , course : calcul-litteral-2019 , quiz_attempts : 1272,,,,, +1552399686, score.stuart@univ-lyon3.fr , score , quiz_attempt_started , attempt : 2 , course : calcul-litteral-2019 , quiz : Exercice 1.2 : QCM , section : 2,,, +1552399686, score.stuart@univ-lyon3.fr , score , quiz_attempt_unfinished , attempt : 2 , course : calcul-litteral-2019 , quiz : Exercice 1.2 : QCM , section : 2,,, +1552399687, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 1.2 : QCM , quiz_attempts : 1272 , section : 2,,, +1552399689, score.stuart@univ-lyon3.fr , score , course_pageview , course : calcul-litteral-2019,,,,,, +1552399691, score.stuart@univ-lyon3.fr , score , course_pageview , course : calcul-litteral-2019 , coursesectionnumber : 3,,,,, +1552399691, score.stuart@univ-lyon3.fr , score , score_update , course : calcul-litteral-2019 , property : points , section : 3 , value : 4000,,, +1552399693, score.stuart@univ-lyon3.fr , score , quiz_moduleview , course : calcul-litteral-2019 , quiz : 146,,,,, +1552399696, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 2.1 : simplification de produit , quiz_attempts : 1271 , section : 3,,, +1552399701, score.stuart@univ-lyon3.fr , score , question_gradedright , attempt : 1 , course : calcul-litteral-2019 , question : 4 , quiz : Exercice 2.1 : simplification de produit , section : 3 ,, +1552399702, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 2.1 : simplification de produit , quiz_attempts : 1271 , section : 3,,, +1552399702, score.stuart@univ-lyon3.fr , score , score_update , course : 214 , property : points , section : , value : 4000,,, +1552399705, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 2.1 : simplification de produit , quiz_attempts : 1271 , section : 3,,, +1552399710, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 2.1 : simplification de produit , quiz_attempts : 1271 , section : 3,,, +1552399710, score.stuart@univ-lyon3.fr , score , score_update , course : 214 , property : points , section : , value : 5000,,, +1552399710, score.stuart@univ-lyon3.fr , score , question_gradedright , attempt : 1 , course : calcul-litteral-2019 , question : 5 , quiz : Exercice 2.1 : simplification de produit , section : 3 ,, +1552399714, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 2.1 : simplification de produit , quiz_attempts : 1271 , section : 3,,, +1552399719, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 2.1 : simplification de produit , quiz_attempts : 1271 , section : 3,,, +1552399719, score.stuart@univ-lyon3.fr , score , score_update , course : 214 , property : points , section : , value : 6000,,, +1552399719, score.stuart@univ-lyon3.fr , score , question_gradedright , attempt : 1 , course : calcul-litteral-2019 , question : 6 , quiz : Exercice 2.1 : simplification de produit , section : 3 ,, +1552399723, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 2.1 : simplification de produit , quiz_attempts : 1271 , section : 3,,, +1552399729, score.stuart@univ-lyon3.fr , score , question_gradedright , attempt : 1 , course : calcul-litteral-2019 , question : 7 , quiz : Exercice 2.1 : simplification de produit , section : 3 ,, +1552399730, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 2.1 : simplification de produit , quiz_attempts : 1271 , section : 3,,, +1552399730, score.stuart@univ-lyon3.fr , score , score_update , course : 214 , property : points , section : , value : 7000,,, +1552399733, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 2.1 : simplification de produit , quiz_attempts : 1271 , section : 3,,, +1552399738, score.stuart@univ-lyon3.fr , score , question_gradedright , attempt : 1 , course : calcul-litteral-2019 , question : 8 , quiz : Exercice 2.1 : simplification de produit , section : 3 ,, +1552399739, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 2.1 : simplification de produit , quiz_attempts : 1271 , section : 3,,, +1552399739, score.stuart@univ-lyon3.fr , score , score_update , course : 214 , property : points , section : , value : 8000,,, +1552399742, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 2.1 : simplification de produit , quiz_attempts : 1271 , section : 3,,, +1552399748, score.stuart@univ-lyon3.fr , score , question_gradedright , attempt : 1 , course : calcul-litteral-2019 , question : 9 , quiz : Exercice 2.1 : simplification de produit , section : 3 ,, +1552399749, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 2.1 : simplification de produit , quiz_attempts : 1271 , section : 3,,, +1552399749, score.stuart@univ-lyon3.fr , score , score_update , course : 214 , property : points , section : , value : 9000,,, +1552399752, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 2.1 : simplification de produit , quiz_attempts : 1271 , section : 3,,, +1552399760, score.stuart@univ-lyon3.fr , score , quiz_pageview , course : calcul-litteral-2019 , quiz : Exercice 2.1 : simplification de produit , quiz_attempts : 1271 , section : 3,,, +1552399760, score.stuart@univ-lyon3.fr , score , score_update , course : 214 , property : points , section : , value : 10000,,, +1552399760, score.stuart@univ-lyon3.fr , score , question_gradedright , attempt : 1 , course : calcul-litteral-2019 , question : 10 , quiz : Exercice 2.1 : simplification de produit , section : 3 ,, +1552399765, score.stuart@univ-lyon3.fr , score , quiz_summaryview , course : calcul-litteral-2019 , quiz : Exercice 2.1 : simplification de produit , quiz_attempts : 1271 , section : 3,,, +1552399767, score.stuart@univ-lyon3.fr , score , quiz_submit , course : calcul-litteral-2019 , quiz : Exercice 2.1 : simplification de produit , quiz_attempts : 1271 , section : 3,,, +1552399767, score.stuart@univ-lyon3.fr , score , quiz_attempt_finished , attempt : 1 , course : calcul-litteral-2019 , quiz : Exercice 2.1 : simplification de produit , section : 3,,, +1552399768, score.stuart@univ-lyon3.fr , score , quiz_review , course : calcul-litteral-2019 , quiz : Exercice 2.1 : simplification de produit , quiz_attempts : 1271 , section : 3,,, +1552399771, score.stuart@univ-lyon3.fr , score , dashboard , ,,,,,, -- GitLab