MDL-57791 analytics: Replace sql queries for API calls

This commit is contained in:
David Monllao 2017-06-14 12:32:27 +02:00
parent acb14d08f6
commit 584ffa4ffc
11 changed files with 110 additions and 29 deletions

View File

@ -162,10 +162,8 @@ class course_dropout extends \core_analytics\local\target\binary {
* @return void
*/
protected function calculate_sample($sampleid, \core_analytics\analysable $course, $starttime = false, $endtime = false) {
global $DB;
$sql = "SELECT ue.* FROM {user_enrolments} ue JOIN {user} u ON u.id = ue.userid WHERE ue.id = :ueid";
$userenrol = $DB->get_record_sql($sql, array('ueid' => $sampleid));
$userenrol = $this->retrieve('user_enrolments', $sampleid);
// We use completion as a success metric only when it is enabled.
$completion = new \completion_info($course->get_course_data());

View File

@ -37,22 +37,22 @@ require_once($CFG->libdir . '/tablelib.php');
class model_logs extends \table_sql {
/**
* @var int
* @var \core_analytics\model
*/
protected $modelid = null;
protected $model = null;
/**
* Sets up the table_log parameters.
*
* @param string $uniqueid unique id of form.
* @param int $modelid model id
* @param \core_analytics\model $model
*/
public function __construct($uniqueid, $modelid) {
public function __construct($uniqueid, $model) {
global $PAGE;
parent::__construct($uniqueid);
$this->modelid = $modelid;
$this->model = $model;
$this->set_attribute('class', 'modellog generaltable generalbox');
$this->set_attribute('aria-live', 'polite');
@ -182,11 +182,9 @@ class model_logs extends \table_sql {
public function query_db($pagesize, $useinitialsbar = true) {
global $DB;
$total = $DB->count_records('analytics_models_log', array('modelid' => $this->modelid));
$total = count($this->model->get_logs());
$this->pagesize($pagesize, $total);
$this->rawdata = $DB->get_records('analytics_models_log', array('modelid' => $this->modelid), 'timecreated DESC', '*',
$this->get_page_start(), $this->get_page_size());
$this->rawdata = $this->model->get_logs($this->get_page_start(), $this->get_page_size());
// Set initial bars.
if ($useinitialsbar) {

View File

@ -41,15 +41,13 @@ class predict_models extends \core\task\scheduled_task {
public function execute() {
global $DB, $OUTPUT, $PAGE;
$models = $DB->get_records_select('analytics_models', 'enabled = 1 AND trained = 1 AND timesplitting IS NOT NULL');
$models = \core_analytics\manager::get_all_models(true, true);
if (!$models) {
mtrace(get_string('errornoenabledandtrainedmodels', 'tool_models'));
return;
}
foreach ($models as $modelobj) {
$model = new \core_analytics\model($modelobj);
foreach ($models as $model) {
$result = $model->predict();
if ($result) {
echo $OUTPUT->heading(get_string('modelresults', 'tool_models', $model->get_target()->get_name()));

View File

@ -41,19 +41,24 @@ class train_models extends \core\task\scheduled_task {
public function execute() {
global $DB, $OUTPUT, $PAGE;
$models = $DB->get_records_select('analytics_models', 'enabled = 1 AND timesplitting IS NOT NULL');
$models = \core_analytics\manager::get_all_models(true);
if (!$models) {
mtrace(get_string('errornoenabledmodels', 'tool_models'));
return;
}
foreach ($models as $modelobj) {
$model = new \core_analytics\model($modelobj);
foreach ($models as $model) {
if ($model->is_static()) {
// Skip models based on assumptions.
continue;
}
if (!$model->get_time_splitting()) {
// Can not train if there is no time splitting method selected.
continue;
}
$result = $model->train();
if ($result) {
echo $OUTPUT->heading(get_string('modelresults', 'tool_models', $model->get_target()->get_name()));

View File

@ -144,7 +144,7 @@ switch ($action) {
}
$renderer = $PAGE->get_renderer('tool_models');
$modellogstable = new \tool_models\output\model_logs('model-' . $model->get_id(), $model->get_id());
$modellogstable = new \tool_models\output\model_logs('model-' . $model->get_id(), $model);
echo $renderer->render_table($modellogstable);
break;
}

View File

@ -34,18 +34,19 @@ defined('MOODLE_INTERNAL') || die();
abstract class by_course extends base {
public function get_courses() {
global $DB;
// Default to all system courses.
if (!empty($this->options['filter'])) {
$it = $this->options['filter'];
$courses = $this->options['filter'];
} else {
// Iterate through all potentially valid courses.
$it = $DB->get_recordset_select('course', 'id != :frontpage', array('frontpage' => SITEID), 'sortorder ASC');
$courses = get_courses();
}
unset($courses[SITEID]);
$analysables = array();
foreach ($it as $course) {
foreach ($courses as $course) {
// Skip the frontpage course.
$analysable = \core_analytics\course::instance($course);
$analysables[$analysable->get_id()] = $analysable;
}
@ -64,7 +65,7 @@ abstract class by_course extends base {
$filesbytimesplitting = array();
// This class and all children will iterate through a list of courses (\core_analytics\course).
$analysables = $this->get_courses();
$analysables = $this->get_courses('all', 'c.sortorder ASC');
foreach ($analysables as $analysableid => $analysable) {
$files = $this->process_analysable($analysable, $includetarget);

View File

@ -63,7 +63,8 @@ class site_courses extends sitewide {
// Getting courses from DB instead of from the site as these samples
// will be stored in memory and we just want the id.
$select = 'id != 1';
$courses = $DB->get_records_select('course', $select, null, '', '*');
$courses = get_courses('all', 'c.sortorder ASC');
unset($courses[SITEID]);
$courseids = array_keys($courses);
$sampleids = array_combine($courseids, $courseids);

View File

@ -50,6 +50,14 @@ class manager {
*/
protected static $alltimesplittings = null;
/**
* Returns all system models that match the provided filters.
*
* @param bool $enabled
* @param bool $trained
* @param \context $predictioncontext
* @return \core_analytics\model[]
*/
public static function get_all_models($enabled = false, $trained = false, $predictioncontext = false) {
global $DB;

View File

@ -1018,6 +1018,19 @@ class model {
return $data;
}
/**
* Returns the model logs data.
*
* @param int $limitfrom
* @param int $limitnum
* @return \stdClass[]
*/
public function get_logs($limitfrom = 0, $limitnum = 0) {
global $DB;
return $DB->get_records('analytics_models_log', array('modelid' => $this->get_id()), 'timecreated DESC', '*',
$limitfrom, $limitnum);
}
/**
* flag_file_as_used
*

View File

@ -0,0 +1,59 @@
<?php
class test_static_target_shortname extends \core_analytics\local\target\binary {
protected $predictions = array();
public function get_analyser_class() {
return '\core_analytics\local\analyser\site_courses';
}
public static function classes_description() {
return array(
'Course fullname first char is A',
'Course fullname first char is not A'
);
}
/**
* We don't want to discard results.
* @return float
*/
protected function min_prediction_score() {
return null;
}
/**
* We don't want to discard results.
* @return array
*/
protected function ignored_predicted_classes() {
return array();
}
public function is_valid_analysable(\core_analytics\analysable $analysable, $fortraining = true) {
// This is testing, let's make things easy.
return true;
}
protected function calculate_sample($sampleid, \core_analytics\analysable $analysable, $starttime = false, $endtime = false) {
global $DB;
$sample = $DB->get_record('course', array('id' => $sampleid));
if ($sample->visible == 0) {
// We skip not-visible courses as a way to emulate the training data / prediction data difference.
// In normal circumstances targets will return null when they receive a sample that can not be
// processed, that same sample may be used for prediction.
// We can not do this in is_valid_analysable because the analysable there is the site not the course.
return null;
}
$firstchar = substr($sample->shortname, 0, 1);
if ($firstchar === 'a') {
return 1;
} else {
return 0;
}
}
}

View File

@ -48,12 +48,12 @@ class core_analytics_prediction_testcase extends advanced_testcase {
public function test_ml_training_and_prediction($timesplittingid, $npredictedranges, $predictionsprocessorclass) {
global $DB;
$this->resetAfterTest(true);
$this->setAdminuser();
set_config('enabled_stores', 'logstore_standard', 'tool_log');
$ncourses = 10;
$this->resetAfterTest(true);
// Generate training data.
$params = array(
'startdate' => mktime(0, 0, 0, 10, 24, 2015),
@ -156,7 +156,7 @@ class core_analytics_prediction_testcase extends advanced_testcase {
*/
public function test_ml_evaluation($modelquality, $ncourses, $expected, $predictionsprocessorclass) {
$this->resetAfterTest(true);
$this->setAdminuser();
set_config('enabled_stores', 'logstore_standard', 'tool_log');
$sometimesplittings = '\core_analytics\local\time_splitting\weekly,' .