public function scatterplotAction($scope = 'concept', $groupingId = '', $debug = false) { $this->view->disable(); // Get our context (this takes care of starting the session, too) $context = $this->getDI()->getShared('ltiContext'); if (!$context->valid) { echo '[{"error":"Invalid lti context"}]'; return; } // Get the list of questions associated with concepts for the given scope and grouping ID $questions = []; switch ($scope) { case "concept": // Filter based on concept $questions = MappingHelper::questionsInConcept($groupingId); break; case "unit": // Filter based on unit $questions = MappingHelper::questionsInConcepts(MappingHelper::conceptsInUnit($groupingId)); break; default: echo '[{"error":"Invalid scope option"}]'; return; break; } if ($debug) { echo "questions for scope {$scope} and grouping {$groupingId}: \n"; print_r($questions); } $classHelper = new ClassHelper(); // Array of questions with more details about each $questionDetails = array(); // Get some info about each question foreach ($questions as $question) { // Check that it's a valid question if ($question != false) { // Get number of attempts $question["attempts"] = MasteryHelper::countAttemptsForQuestion($context->getUserName(), $question["OA Quiz ID"], $question["Question Number"], $debug); $question["scaledAttemptScore"] = $classHelper->calculateScaledAttemptScoreForQuestion($question["attempts"], $question["OA Quiz ID"], $question["Question Number"], $debug); // Get amount of associated videos watched // Note that question ID is being used instead of assessment ID and question number, since we're searching the csv mapping and not dealing with assessment statements here $question["videoPercentage"] = MasteryHelper::calculateUniqueVideoPercentageForQuestion($context->getUserName(), $questionId); $questionDetails[] = $question; } } /* function randomPoint($group, $q) { // Randomly return outliers if (rand(0,30) == 5) { return [$group, $q["quizNumber"], $q["questionNumber"], rand(-10000, 1000), rand(-10000, 1000)]; } return [$group, $q["quizNumber"], $q["questionNumber"], rand(-100, 100), rand(-100, 100)]; } $result = []; // For now, return random points based on number of questions $numPoints = count($questionDetails); //foreach ($questionDetails as $q) { //$result [] = //} // for ($i=0; $i<$numPoints; $i++) { $result []= randomPoint("student", $questionDetails[$i]); for ($j=0; $j<10; $j++) { $result []= randomPoint("class", $questionDetails[$i]); } } $xValues = array_map(function($point) { return $point[3]; }, $result); $yValues = array_map(function($point) { return $point[4]; }, $result); // TODO check that xValues and yValues have a length, otherwise statshelper will spit out errors // Perform some statistics grossness // Remove any outliers for both axes, based on 1.5*IQR // Cap and floor x outliers $xStats = StatsHelper::boxPlotValues($xValues); $result = array_map(function($point) use ($xStats) { $x = $point[3]; // Floor upper outliers if ($x > $xStats['q3'] + (1.5 * $xStats['iqr'])) { $x = $xStats['q3'] + (.5 * $xStats['iqr']); } // Cap lower outliers if ($x < $xStats['q1'] - (1.5 * $xStats['iqr'])) { $x = $xStats['q1'] - (.5 * $xStats['iqr']); } $point[3] = $x; return $point; }, $result); // Scale all the scores from 0 to 10 // TODO // Cap and floor y outliers $yStats = StatsHelper::boxPlotValues($yValues); $result = array_map(function($point) use ($yStats) { $y = $point[4]; // Floor upper outliers if ($y > $yStats['q3'] + (1.5 * $yStats['iqr'])) { $y = $yStats['q3'] + (.5 * $yStats['iqr']); } // Cap lower outliers if ($y < $yStats['q1'] - (1.5 * $yStats['iqr'])) { $y = $yStats['q1'] - (.5 * $yStats['iqr']); } $point[4] = $y; return $point; }, $result); //print_r($result); //print_r($yStats); //die(); */ // X = video percentage, Y = question attempts $headerRow = ["group", "quiz_number", "question_number", "x", "y"]; $result = array_map(function ($q) { return ["student", $q["OA Quiz ID"], $q["Question Number"], $q["videoPercentage"], $q["scaledAttemptScore"]]; }, $questionDetails); if ($debug) { echo "question details for scope {$scope} and grouping {$groupingId}: \n"; print_r($questionDetails); print_r($result); } // Output data as csv so that we only have to send header information once for so many points if (!$debug) { header("Content-Type: text/csv"); } $output = fopen("php://output", "w"); fputcsv($output, $headerRow); foreach ($result as $row) { fputcsv($output, $row); // here you can change delimiter/enclosure } fclose($output); }