* @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());
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');
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) {
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()));
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()));
}
$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;
}
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;
}
$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);
// 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);
*/
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;
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
*
--- /dev/null
+<?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;
+ }
+ }
+}
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),
*/
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,' .