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 getStudentData(scenarioID, courseid):
    """
    Collect the ids and timestamps of the students that completed a scenario and for each student, get the pages they looked at in the 14 days before that.
    \n
    :param scenarioID: The scenario to perform the analysis on \t
    :type scenarioID: string \n
    :param courseid: The courseid of the course the scenario belongs to \t
    :type courseid: string \n
    :returns: void \t
    :rtype: void \n
    """
    data = getDTdata(scenarioID)
    studentdata = ""
    if utils.hasError(data):
        studentdata = data
    else:
        aggregated = groupByStudent(data)
        studentdata = list(
            map(
                lambda data, actor: {
                    "score": data["score"],
                    "pages": getPageViewData(courseid, actor, data["timestamp"]
                                             )
                }, list(aggregated.values()), list(aggregated.keys())))
    d = os.path.dirname(os.path.realpath(__file__))
    f = open(f"{d}/response_{scenarioID}_{courseid}.json", "w")
    f.write(json.dumps(studentdata))
    f.close()
Esempio n. 3
0
 def test_hasError(self):
     """
     Asserts that the error function detects errors when they are preesent and does not detect them when they are not present
     """
     # check if the result matches the desired output
     self.assertEqual(False, utils.hasError({}))
     self.assertEqual(False,
                      utils.hasError({
                          "key": "value",
                          "key2": "value2"
                      }))
     self.assertEqual(True,
                      utils.hasError({
                          "key1": "data",
                          "error": "errormsg"
                      }))
     self.assertEqual(True, utils.hasError({"error": "errormsg"}))
Esempio n. 4
0
def countViewedStatements(courseid):
    """
    Count the amount of 'viewed' statements from a specific course \n
    :type courseid: str \n
    :returns: The amount of statements in a dictionary with 'count' as the only key \n
    :rtype: dict<string:int> \n
    """
    statements = getViewHistory(courseid)
    if utils.hasError(statements):
        return statements
    return {"count": len(statements)}
def bestAttemptStatistics(scenarioID):
    """
    Calculates the statistics for the best scores of each of the students that completed the scenario.
    For each of the students, the highest score from all their attempts will be selected and for the list of those scores,
    the statistics will be calculated
    \n
    :param scenarioID: The id of the scenario to calculate the statistics for \t
    :type scenarioID: int \n
    :returns: Statistics data with the following structure {average, min, max, ...} | {error} \t
    :rtype: Dict<string, int> | Dict<string, string>\n
    """
    data = runQuery(scenarioID)
    if utils.hasError(data):
        return data
    attempts = groupByStudent(data).values()
    attempts = map(max, attempts)
    return utils.getSetStatistics(attempts)
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 betweenStudentsDifference(scenarioID):
    """
    Calculates the statistics for the scores of students within an attempt.
    For example: if there are three students of which one does a scenario once and two of them do it twice,
    the result will be a list of two elements, the first element are the statistics for the first attempt of all three students
    and the second element are the statistics for the two students that did a second attempt.
    \n
    :param scenarioID: The id of the scenario to calculate the statistics for \t
    :type scenarioID: int \n
    :returns: Statistics data with the following structure [{average, min, max, ...}] \t
    :rtype: List<Dict<string, int>> \n
    """
    data = runQuery(scenarioID)
    if utils.hasError(data):
        return data
    attempts = groupByStudent(data)
    attempts = utils.transposeLists(attempts.values())
    attempts = map(utils.getSetStatistics, attempts)
    return list(attempts)
def betweenStudentAttemptDifference(scenarioID):
    """
    Calculates the statistics for the scores of students between each attempt.
    For example, if there are three students of which one does a scenario twice and two of them do it three times,
    the result will be a list of two elements, the first element are the statistics for the improvement between attempt 1 and attempt 2 for all three students
    and the second element are the statistics for the improvement between attempt 2 and 3 for the two students that did a third attempt
    \n
    :param scenarioID: The id of the scenario to calculate the statistics for \t
    :type scenarioID: int \n
    :returns: Statistics data with the following structure [{average, min, max, ...}] | {error} \t
    :rtype: List<Dict<string, int>> | Dict<string, string>\n
    """
    data = runQuery(scenarioID)
    if utils.hasError(data):
        return data
    attempts = groupByStudent(data).values()
    attempts = [utils.getSequenceDifference(lst) for lst in attempts if len(lst) > 1]
    attempts = utils.transposeLists(attempts)
    attempts = map(utils.getSetStatistics, attempts)
    return list(attempts)
def dtViewedPages(scenarioID, courseid):
    """
    Calculates the optimal regression model and returns the coefficients of the pages.
    \n
    :param scenarioID: The id of the scenario to calculate the correlation for \t
    :type scenarioID: string \n
    :param courseid: The course to correlate the scenario with. \t
    :type courseid: string \n
    :returns: A dictionary with the coefficients and some metadata \t
    :rtype: dict \n
    """
    d = os.path.dirname(os.path.realpath(__file__))
    if (not os.path.isfile(f"{d}/response_{scenarioID}_{courseid}.json")):
        getStudentData(scenarioID, courseid)
        return dtViewedPages(scenarioID, courseid)

    studentdata = loadStudentData(scenarioID, courseid)

    if utils.hasError(studentdata):
        return studentdata

    featuredata = getFeatures(studentdata)
    features = featuredata["features"]
    scores = featuredata["scores"]

    options = [0.4, 0.45, 0.5, 0.55, 0.6, 0.65, 0.7, 0.75, 0.8]

    resultOptions = list(
        map(lambda percentage: linearRegression(features, scores, percentage),
            options))

    best = resultOptions[0]
    for result in resultOptions:
        if result["predRMSE"] < best["predRMSE"]:
            best = result

    return best
Esempio n. 11
0
def analyseModuleVisitTime(courseID):
    """
    Queries the LRS and performs analysis on the time spent by students
    visiting course pages based on a moodle courseID. \n
    :param courseID: Moodle ID of the course to analyse \t
    :type courseID: str \n
    :returns: json formatted data with the following structure [pageID:[(date, durationSum, count)]] \t
    :rtype: json formatted string \n
    """
    # actual analysis happens in these two functions
    queryResult = getStatementData(courseID)
    if (utils.hasError(queryResult)):
        return queryResult  # send data back without any analysis in this case
    data = queryDataToDict(queryResult)
    pageVisitTime = dictionaryActorLoop(data)

    # formatting for response
    result = dict()
    for pageid in pageVisitTime.keys():
        if (pageid ==
                f'{settings.MOODLE_BASE_URL}/course/view.php?id={courseID}'):
            continue

        # cast datetime types to string and group data together as triples
        res = []
        for date in pageVisitTime[pageid].keys():
            datestr = date.strftime('%Y-%m-%d')
            time, count = pageVisitTime[pageid][date]
            res.append({
                "date": datestr,
                "time": str(time.total_seconds()),
                "count": count
            })

        result[pageid] = res

    return result
Esempio n. 12
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