Skip to content

Commit

Permalink
MDL-58859 analytics: Analytics API added to core
Browse files Browse the repository at this point in the history
Part of MDL-57791 epic.
  • Loading branch information
David Monllao committed Jul 24, 2017
1 parent efaf853 commit 369389c
Show file tree
Hide file tree
Showing 89 changed files with 8,889 additions and 0 deletions.
87 changes: 87 additions & 0 deletions admin/settings/analytics.php
@@ -0,0 +1,87 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
* Adds settings links to admin tree.
*
* @package core_analytics
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

defined('MOODLE_INTERNAL') || die();

if ($hassiteconfig) {
$settings = new admin_settingpage('analyticssettings', new lang_string('analyticssettings', 'analytics'));
$ADMIN->add('appearance', $settings);

if ($ADMIN->fulltree) {
// Select the site prediction's processor.
$predictionprocessors = \core_analytics\manager::get_all_prediction_processors();
$predictors = array();
foreach ($predictionprocessors as $fullclassname => $predictor) {
$pluginname = substr($fullclassname, 1, strpos($fullclassname, '\\', 1) - 1);
$predictors[$fullclassname] = new lang_string('pluginname', $pluginname);
}
$settings->add(new \core_analytics\admin_setting_predictor('analytics/predictionsprocessor',
new lang_string('predictionsprocessor', 'analytics'), new lang_string('predictionsprocessor_help', 'analytics'),
'\mlbackend_php\processor', $predictors)
);

// Enable/disable time splitting methods.
$alltimesplittings = \core_analytics\manager::get_all_time_splittings();

$timesplittingoptions = array();
$timesplittingdefaults = array('\\core_analytics\\local\\time_splitting\\quarters_accum',
'\\core_analytics\\local\\time_splitting\\quarters');
foreach ($alltimesplittings as $key => $timesplitting) {
$timesplittingoptions[$key] = $timesplitting->get_name();
}
$settings->add(new admin_setting_configmultiselect('analytics/timesplittings',
new lang_string('enabledtimesplittings', 'analytics'), new lang_string('enabledtimesplittings_help', 'analytics'),
$timesplittingdefaults, $timesplittingoptions)
);

// Predictions processor output dir.
$defaultmodeloutputdir = rtrim($CFG->dataroot, '/') . DIRECTORY_SEPARATOR . 'models';
$settings->add(new admin_setting_configdirectory('analytics/modeloutputdir', new lang_string('modeloutputdir', 'analytics'),
new lang_string('modeloutputdirinfo', 'analytics'), $defaultmodeloutputdir));
$studentdefaultroles = [];
$teacherdefaultroles = [];

// Student and teacher roles.
$allroles = role_fix_names(get_all_roles());
$rolechoices = [];
foreach ($allroles as $role) {
$rolechoices[$role->id] = $role->localname;

if ($role->shortname == 'student') {
$studentdefaultroles[] = $role->id;
} else if ($role->shortname == 'teacher') {
$teacherdefaultroles[] = $role->id;
} else if ($role->shortname == 'editingteacher') {
$teacherdefaultroles[] = $role->id;
}
}

$settings->add(new admin_setting_configmultiselect('analytics/teacherroles', new lang_string('teacherroles', 'analytics'),
'', $teacherdefaultroles, $rolechoices));

$settings->add(new admin_setting_configmultiselect('analytics/studentroles', new lang_string('studentroles', 'analytics'),
'', $studentdefaultroles, $rolechoices));

}
}
57 changes: 57 additions & 0 deletions analytics/classes/admin_setting_predictor.php
@@ -0,0 +1,57 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
*
* @package core_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

namespace core_analytics;

defined('MOODLE_INTERNAL') || die();

require_once(__DIR__ . '/../../lib/adminlib.php');

class admin_setting_predictor extends \admin_setting_configselect {

/**
* Builds HTML to display the control.
*
* The main purpose of this is to display a warning if the selected predictions processor is not ready.
* @param string $data Unused
* @param string $query
* @return string HTML
*/
public function output_html($data, $query='') {
global $CFG, $OUTPUT;

$html = '';

// Calling it here without checking if it is ready because we check it below and show it as a controlled case.
$selectedprocessor = \core_analytics\manager::get_predictions_processor($data, false);

$isready = $selectedprocessor->is_ready();
if ($isready !== true) {
$html .= $OUTPUT->notification(get_string('errorprocessornotready', 'analytics', $isready));
}

$html .= parent::output_html($data, $query);
return $html;
}
}
45 changes: 45 additions & 0 deletions analytics/classes/analysable.php
@@ -0,0 +1,45 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
*
* @package core_analytics
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

namespace core_analytics;

defined('MOODLE_INTERNAL') || die();

/**
*
* @package core_analytics
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
interface analysable {

const MAX_TIME = 9999999999;

public function get_id();

public function get_context();

public function get_start();

public function get_end();
}
156 changes: 156 additions & 0 deletions analytics/classes/calculable.php
@@ -0,0 +1,156 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
* Calculable dataset items abstract class.
*
* @package core_analytics
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

namespace core_analytics;

defined('MOODLE_INTERNAL') || die();

/**
* Calculable dataset items abstract class.
*
* @package core_analytics
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class calculable {

/**
* Returns a visible name for the indicator.
*
* Used as column identificator.
*
* Defaults to the indicator class name.
*
* @return string
*/
public static function get_name() {
return get_called_class();
}

/**
* Returns the number of weeks a time range contains.
*
* Useful for calculations that depend on the time range duration.
*
* @param int $starttime
* @param int $endtime
* @return float
*/
protected function get_time_range_weeks_number($starttime, $endtime) {
if ($endtime <= $starttime) {
throw new \coding_exception('End time timestamp should be greater than start time.');
}

$diff = $endtime - $starttime;

// No need to be strict about DST here.
return $diff / WEEKSECS;
}

/**
* Limits the calculated value to the minimum and maximum values.
*
* @param float $calculatedvalue
* @return float|null
*/
protected function limit_value($calculatedvalue) {
return max(min($calculatedvalue, static::get_max_value()), static::get_min_value());
}

/**
* Classifies the provided value into the provided range according to the ranges predicates.
*
* Use:
* - eq as 'equal'
* - ne as 'not equal'
* - lt as 'lower than'
* - le as 'lower or equal than'
* - gt as 'greater than'
* - ge as 'greater or equal than'
*
* @param int|float $value
* @param array $ranges e.g. [ ['lt', 20], ['ge', 20] ]
* @return void
*/
protected function classify_value($value, $ranges) {

// To automatically return calculated values from min to max values.
$rangeweight = (static::get_max_value() - static::get_min_value()) / (count($ranges) - 1);

foreach ($ranges as $key => $range) {

$match = false;

if (count($range) != 2) {
throw \coding_exception('classify_value() $ranges array param should contain 2 items, the predicate ' .
'e.g. greater (gt), lower or equal (le)... and the value.');
}

list($predicate, $rangevalue) = $range;

switch ($predicate) {
case 'eq':
if ($value == $rangevalue) {
$match = true;
}
break;
case 'ne':
if ($value != $rangevalue) {
$match = true;
}
break;
case 'lt':
if ($value < $rangevalue) {
$match = true;
}
break;
case 'le':
if ($value <= $rangevalue) {
$match = true;
}
break;
case 'gt':
if ($value > $rangevalue) {
$match = true;
}
break;
case 'ge':
if ($value >= $rangevalue) {
$match = true;
}
break;
default:
throw new \coding_exception('Unrecognised predicate ' . $predicate . '. Please use eq, ne, lt, le, ge or gt.');
}

// Calculate and return a linear calculated value for the provided value.
if ($match) {
return round(static::get_min_value() + ($rangeweight * $key), 2);
}
}

throw new \coding_exception('The provided value "' . $value . '" can not be fit into any of the provided ranges, you ' .
'should provide ranges for all possible values.');
}
}

0 comments on commit 369389c

Please sign in to comment.