def generateData(courseid): """ Generate the data for the quiz participation analysis \n :type courseid: str \n :returns: dictionary containing the completion percentage for each quiz \n :rtype: dict<str,double> \n """ data = runQuery(courseid) if utils.hasError(data): return data # The 'activity' of submitting a quiz is an 'attempt'. This mapping gets the actual quiz the actor submitted quizzes = map( lambda stm: { "actor": stm["actor"], "quiz": list( filter(lambda uri: "/mod/quiz/view.php" in uri, stm[ "contextActivities"]))[0] }, data) # Group by the quiz and create a set of all the (distinct) actors that did the quiz quizzes = utils.groupOn(quizzes, lambda x: x["quiz"], lambda x: {x["actor"]}, lambda total, x: total.union({x["actor"]})) # For each set, the length is the amount of submissions from different actors return utils.mapDict(quizzes, len)
def generateViewedHistory(courseid): """ Count the amount of 'viewed' statements from a specific course per activity per date \n :type courseid: str \n :returns: A dictionary of pages with for each page a list of dictionaries containing the date and the count \n :rtype: dict<string:list<dict<string,mixed>>> \n """ data = getViewHistory(courseid) if utils.hasError(data): return data # Group the statements by activity (emodule) pages = utils.groupOn( data, lambda x: x["activity"], lambda x: [utils.getDate(x["timestamp"])], lambda total, x: total + [utils.getDate(x["timestamp"])] ) # For each emodule, group by date and count the amount of results for each date pages = utils.mapDict( pages, lambda lst: utils.dictAsKvParray( utils.groupOn( lst, utils.id, lambda x: 1, lambda total, x: total + 1 ), "date", "count" ) ) return pages
def groupByStudent(dataset): """ Groups a dataset based on the student id by concatenating the scores of those students into a list \n :param dataset: A list of dictionaries, each of them containing at least a "actor" and a "result" key \t :type dataset: [{actor, result, ...}] \n :returns: A dictionary of student id's and the scores they aquired, in the reversed order as the input dataset \t :rtype: Dict<string, List<int>> \n """ newDataset = utils.groupOn( dataset, lambda x: x["actor"], lambda x: [utils.getAverageScore(x["result"])], lambda total, x: [utils.getAverageScore(x["result"])] + total ) return newDataset
def generateData(courseid): """ Generate the data for the assignment participation analysis \n :type courseid: str \n :returns: dictionary containing the completion percentage for each assignment \n :rtype: dict<str,double> \n """ data = runQuery(courseid) if utils.hasError(data): return data # Group the assignments based on their id (activity) and create a set of distinct actors that completed them assignments = utils.groupOn(data, lambda x: x["activity"], lambda x: {x["actor"]}, lambda total, x: total.union({x["actor"]})) # The length of each set is the amount of different actors that completed the assignment return utils.mapDict(assignments, len)
def generateData(courseid): """ Generate the data for the e-module distinct view analysis \n :type courseid: str \n :returns: dictionary containing the amount of distinct views for each emodule \n :rtype: dict<str,int> \n """ data = runQuery(courseid) if utils.hasError(data): return data # Group de data on activity and create a set of distinct actors for each activity pages = utils.groupOn(data, lambda x: x["activity"], lambda x: {x["actor"]}, lambda total, x: total.union({x["actor"]})) # The length of the set of actors is the amount of views from different actors return utils.mapDict(pages, len)
def groupByStudent(dataset): """ Calculate the total score of each scenario for each student and group the scenario completions by student \n :param dataset: A list of xAPI statements, each containing at least the actor, the timestamp and the result \t :type dataset: [dict] \n :returs: A dictionary, with for each student the score and timestamp of their first attempt \t :rtype: dict<float> \n """ newDataset = utils.groupOn( dataset, lambda x: x["actor"], lambda x: { "timestamp": x["timestamp"], "score": utils.getAverageScore(x["result"]) }, # lambda total, x: {"timestamp": x["timestamp"], "score": getAverageScore(x["result"])} if getAverageScore(x["result"]) > total["score"] else total lambda total, x: { "timestamp": x["timestamp"], "score": utils.getAverageScore(x["result"]) }) return newDataset
def getPageViewData(courseid, actorid, untiltime): """ For one student, get all the pages viewed between the untiltime and 14 days earlier \n :param courseid: The course to which the pages should belong \t :type courseid: string \n :param actorid: The full LRS id of the student to get the page views for \t :type actorid: string (stringified json) \n :param untiltime: A "YYYY-MM-DDThh:mm:ssZ" formatted timestamp \t :type untiltime: string (datetime) \n :returns: A dictionary of pages visited pages and for each page the amount of visits \t :rtype: dict \n """ def stmInRange(lower, upper, stm): timestamp = datetime.fromisoformat(stm["timestamp"].replace( ".000Z", "")) return lower < timestamp and timestamp < upper untiltime = datetime.fromisoformat(untiltime.replace("Z", "")) sincetime = untiltime - timedelta(days=14) querydata = (lrs.Query().select( lrs.Attr.ACTIVITY, lrs.Attr.TIMESTAMP).where( lrs.Attr.VERB, lrs.IS, "http://id.tincanapi.com/verb/viewed").where( lrs.Attr.CONTEXTACTIVITY, lrs.IS, f"http://localhost/course/view.php?id={courseid}").where( lrs.Attr.ACTIVITY, lrs.CONTAINS, "/mod/page/view.php").where(lrs.Attr.ACTOR, lrs.IS, actorid).execute()) querydata = filter(lambda stm: stmInRange(sincetime, untiltime, stm), querydata) querydata = map(lambda stm: utils.getIdFromUrl(stm["activity"]), querydata) querydata = utils.groupOn(querydata, utils.id, lambda x: 1, lambda total, x: total + 1) return querydata
def test_groupOn(self): """ Asserts that the groupOn function has the correct output for both empty and non-empty inputs and for multiple operators """ # create test and validation data testInput = [ { "key": "a", "value": 1 }, { "key": "b", "value": 2 }, { "key": "c", "value": 3 }, { "key": "a", "value": 4 }, { "key": "b", "value": 5 }, { "key": "c", "value": 6 }, { "key": "a", "value": 7 }, { "key": "b", "value": 8 }, { "key": "c", "value": 9 }, ] testEmpty = [] summedCorrect = {"a": 12, "b": 15, "c": 18} concattedCorrect = {"a": [1, 4, 7], "b": [2, 5, 8], "c": [3, 6, 9]} emptyCorrect = {} # test function sumResult = utils.groupOn(testInput, lambda x: x["key"], lambda x: x["value"], lambda total, x: total + x["value"]) concattedResult = utils.groupOn(testInput, lambda x: x["key"], lambda x: [x["value"]], lambda total, x: total + [x["value"]]) emptyResult = utils.groupOn(testEmpty, lambda x: x["key"], lambda x: x["value"], lambda total, x: total + x["value"]) # check if the result matches the desired result self.assertEqual(summedCorrect, sumResult) self.assertEqual(concattedCorrect, concattedResult) self.assertEqual(emptyCorrect, emptyResult)