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)
Example #2
0
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
Example #8
0
    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)