From d126f838066758d89e8d63ed2dc5802039520e8f Mon Sep 17 00:00:00 2001 From: David Monllao Date: Thu, 10 Aug 2017 13:43:17 +0200 Subject: [PATCH] MDL-59661 analytics: Export models training data --- .../analytics/classes/output/models_list.php | 9 ++++++ .../tool/analytics/lang/en/tool_analytics.php | 6 +++- admin/tool/analytics/model.php | 19 +++++++++++ analytics/classes/dataset_manager.php | 32 +++++++++++++++++++ analytics/classes/model.php | 13 ++++++++ 5 files changed, 78 insertions(+), 1 deletion(-) diff --git a/admin/tool/analytics/classes/output/models_list.php b/admin/tool/analytics/classes/output/models_list.php index 36f1febbcae46..d52459502c95c 100644 --- a/admin/tool/analytics/classes/output/models_list.php +++ b/admin/tool/analytics/classes/output/models_list.php @@ -139,6 +139,7 @@ public function export_for_template(\renderer_base $output) { $actionsmenu->add($icon); } + // Get predictions. if ($modeldata->enabled && !empty($modeldata->timesplitting)) { $url = new \moodle_url('model.php', array('action' => 'getpredictions', 'id' => $model->get_id())); $icon = new \action_menu_link_secondary($url, new \pix_icon('i/notifications', @@ -154,6 +155,14 @@ public function export_for_template(\renderer_base $output) { $actionsmenu->add($icon); } + // Export training data. + if (!$model->is_static() && $model->is_trained()) { + $url = new \moodle_url('model.php', array('action' => 'export', 'id' => $model->get_id())); + $icon = new \action_menu_link_secondary($url, new \pix_icon('i/export', + get_string('exporttrainingdata', 'tool_analytics')), get_string('export', 'tool_analytics')); + $actionsmenu->add($icon); + } + $modeldata->actions = $actionsmenu->export_for_template($output); $data->models[] = $modeldata; diff --git a/admin/tool/analytics/lang/en/tool_analytics.php b/admin/tool/analytics/lang/en/tool_analytics.php index eba2a8f980b85..4922a512486d1 100644 --- a/admin/tool/analytics/lang/en/tool_analytics.php +++ b/admin/tool/analytics/lang/en/tool_analytics.php @@ -36,13 +36,16 @@ $string['errorcantenablenotimesplitting'] = 'You need to select a time splitting method before enabling the model'; $string['errornoenabledandtrainedmodels'] = 'There are not enabled and trained models to predict'; $string['errornoenabledmodels'] = 'There are not enabled models to train'; +$string['errornoexport'] = 'Only trained models can be exported'; $string['errornostaticedit'] = 'Models based on assumptions can not be edited'; $string['errornostaticevaluated'] = 'Models based on assumptions can not be evaluated, they are always 100% correct according to how they were defined'; $string['errornostaticlog'] = 'Models based on assumptions can not be evaluated, there is no preformance log'; +$string['errortrainingdataexport'] = 'The model training data could not be exported'; $string['evaluate'] = 'Evaluate'; $string['evaluatemodel'] = 'Evaluate model'; $string['evaluationinbatches'] = 'The site contents are calculated and stored in batches, during evaluation you can stop the process at any moment, the next time you run it it will continue from the point you stopped it.'; -$string['trainandpredictmodel'] = 'Training model and calculating predictions'; +$string['export'] = 'Export'; +$string['exporttrainingdata'] = 'Export training data'; $string['getpredictionsresultscli'] = 'Results using {$a->name} (id: {$a->id}) course duration splitting'; $string['getpredictionsresults'] = 'Results using {$a->name} course duration splitting'; $string['extrainfo'] = 'Info'; @@ -68,6 +71,7 @@ $string['samestartdate'] = 'Current start date is good'; $string['sameenddate'] = 'Current end date is good'; $string['target'] = 'Target'; +$string['trainandpredictmodel'] = 'Training model and calculating predictions'; $string['trainingprocessfinished'] = 'Training process finished'; $string['trainingresults'] = 'Training results'; $string['trainmodels'] = 'Train models'; diff --git a/admin/tool/analytics/model.php b/admin/tool/analytics/model.php index 9ef3c3d49d95a..daf62cf5ba81f 100644 --- a/admin/tool/analytics/model.php +++ b/admin/tool/analytics/model.php @@ -57,6 +57,9 @@ case 'disable': $title = get_string('disable'); break; + case 'export': + $title = get_string('export', 'tool_analytics'); + break; default: throw new moodle_exception('errorunknownaction', 'analytics'); @@ -162,6 +165,22 @@ $modellogstable = new \tool_analytics\output\model_logs('model-' . $model->get_id(), $model); echo $renderer->render_table($modellogstable); break; + + case 'export': + + if ($model->is_static() || !$model->is_trained()) { + throw new moodle_exception('errornoexport', 'tool_analytics'); + } + + $file = $model->get_training_data(); + if (!$file) { + redirect(new \moodle_url('/admin/tool/analytics/index.php'), get_string('errortrainingdataexport', 'tool_analytics'), + null, \core\output\notification::NOTIFY_ERROR); + } + + $filename = 'training-data.' . $model->get_id() . '.' . time() . '.csv'; + send_file($file, $filename, null, 0, false, true); + break; } echo $OUTPUT->footer(); diff --git a/analytics/classes/dataset_manager.php b/analytics/classes/dataset_manager.php index 052b7edb1b70d..882d8dc584b60 100644 --- a/analytics/classes/dataset_manager.php +++ b/analytics/classes/dataset_manager.php @@ -91,6 +91,7 @@ class dataset_manager { /** * Constructor method. * + * @throws \coding_exception * @param int $modelid * @param int $analysableid * @param string $timesplittingid @@ -320,6 +321,37 @@ public static function merge_datasets(array $files, $modelid, $timesplittingid, return $fs->create_file_from_pathname($filerecord, $tmpfilepath); } + /** + * Exports the model training data. + * + * @param int $modelid + * @param string $timesplittingid + * @return \stored_file|false + */ + public static function export_training_data($modelid, $timesplittingid) { + + $fs = get_file_storage(); + + $contextid = \context_system::instance()->id; + $filepath = '/timesplitting/' . self::clean_time_splitting_id($timesplittingid) . '/'; + + $files = $fs->get_directory_files($contextid, 'analytics', self::LABELLED_FILEAREA, $modelid, + $filepath, true, false); + + // Discard evaluation files. + foreach ($files as $key => $file) { + if ($file->get_filename() === self::EVALUATION_FILENAME) { + unset($files[$key]); + } + } + + if (empty($files)) { + return false; + } + + return self::merge_datasets($files, $modelid, $timesplittingid, self::EXPORT_FILEAREA); + } + /** * Returns the dataset file data structured by sampleids using the indicators and target column names. * diff --git a/analytics/classes/model.php b/analytics/classes/model.php index aae6682d56532..f1c2708c53317 100644 --- a/analytics/classes/model.php +++ b/analytics/classes/model.php @@ -1233,6 +1233,19 @@ public function get_logs($limitfrom = 0, $limitnum = 0) { $limitfrom, $limitnum); } + /** + * Merges all training data files into one and returns it. + * + * @return \stored_file|false + */ + public function get_training_data() { + + \core_analytics\manager::check_can_manage_models(); + + $timesplittingid = $this->get_time_splitting()->get_id(); + return \core_analytics\dataset_manager::export_training_data($this->get_id(), $timesplittingid); + } + /** * Flag the provided file as used for training or prediction. *