diff --git a/admin/tool/analytics/amd/build/model.min.js b/admin/tool/analytics/amd/build/model.min.js
index 180805da52ea2..9e958dbd39538 100644
--- a/admin/tool/analytics/amd/build/model.min.js
+++ b/admin/tool/analytics/amd/build/model.min.js
@@ -1 +1 @@
-define(["jquery","core/str","core/log","core/notification","core/modal_factory","core/modal_events","core/templates"],function(a,b,c,d,e,f,g){var h={clear:{title:{key:"clearpredictions",component:"tool_analytics"},body:{key:"clearmodelpredictions",component:"tool_analytics"}},"delete":{title:{key:"delete",component:"tool_analytics"},body:{key:"deletemodelconfirmation",component:"tool_analytics"}}},i=function(b){return a(b.closest("tr")[0]).find("span.target-name").text()};return{confirmAction:function(g,j){a('[data-action-id="'+g+'"]').on("click",function(g){g.preventDefault();var k=a(g.currentTarget);if("undefined"==typeof h[j])return void c.error('Action "'+j+'" is not allowed.');var l=[h[j].title,h[j].body];l[1].param=i(k);var m=b.get_strings(l),n=e.create({type:e.types.SAVE_CANCEL});a.when(m,n).then(function(a,b){return b.setTitle(a[0]),b.setBody(a[1]),b.setSaveButtonText(a[0]),b.getRoot().on(f.save,function(){window.location.href=k.attr("href")}),b.show(),b}).fail(d.exception)})},selectEvaluationMode:function(c,h){a('[data-action-id="'+c+'"]').on("click",function(c){c.preventDefault();var i=a(c.currentTarget);if(!h)return void(window.location.href=i.attr("href"));var j=b.get_strings([{key:"evaluatemodel",component:"tool_analytics"},{key:"evaluationmode",component:"tool_analytics"}]),k=e.create({type:e.types.SAVE_CANCEL}),l=g.render("tool_analytics/evaluation_mode_selection",{});a.when(j,k).then(function(b,c){return c.getRoot().on(f.hidden,c.destroy.bind(c)),c.setTitle(b[1]),c.setSaveButtonText(b[0]),c.setBody(l),c.getRoot().on(f.save,function(){var b=a("input[name='evaluationmode']:checked").val();"trainedmodel"==b&&i.attr("href",i.attr("href")+"&mode=trainedmodel"),window.location.href=i.attr("href")}),c.show(),c}).fail(d.exception)})}}});
\ No newline at end of file
+define(["jquery","core/str","core/log","core/notification","core/modal_factory","core/modal_events","core/templates"],function(a,b,c,d,e,f,g){var h={clear:{title:{key:"clearpredictions",component:"tool_analytics"},body:{key:"clearmodelpredictions",component:"tool_analytics"}},"delete":{title:{key:"delete",component:"tool_analytics"},body:{key:"deletemodelconfirmation",component:"tool_analytics"}}},i=function(b){return a(b.closest("tr")[0]).find("span.target-name").text()};return{confirmAction:function(g,j){a('[data-action-id="'+g+'"]').on("click",function(g){g.preventDefault();var k=a(g.currentTarget);if("undefined"==typeof h[j])return void c.error('Action "'+j+'" is not allowed.');var l=[h[j].title,h[j].body];l[1].param=i(k);var m=b.get_strings(l),n=e.create({type:e.types.SAVE_CANCEL});a.when(m,n).then(function(a,b){return b.setTitle(a[0]),b.setBody(a[1]),b.setSaveButtonText(a[0]),b.getRoot().on(f.save,function(){window.location.href=k.attr("href")}),b.show(),b}).fail(d.exception)})},selectEvaluationMode:function(c,h){a('[data-action-id="'+c+'"]').on("click",function(c){c.preventDefault();var i=a(c.currentTarget);if(!h)return void(window.location.href=i.attr("href"));var j=b.get_strings([{key:"evaluatemodel",component:"tool_analytics"},{key:"evaluationmode",component:"tool_analytics"}]),k=e.create({type:e.types.SAVE_CANCEL}),l=g.render("tool_analytics/evaluation_mode_selection",{});a.when(j,k).then(function(b,c){return c.getRoot().on(f.hidden,c.destroy.bind(c)),c.setTitle(b[1]),c.setSaveButtonText(b[0]),c.setBody(l),c.getRoot().on(f.save,function(){var b=a("input[name='evaluationmode']:checked").val();"trainedmodel"==b&&i.attr("href",i.attr("href")+"&mode=trainedmodel"),window.location.href=i.attr("href")}),c.show(),c}).fail(d.exception)})},selectExportOptions:function(c,h){a('[data-action-id="'+c+'"]').on("click",function(c){c.preventDefault();var i=a(c.currentTarget);if(!h)return i.attr("href",i.attr("href")+"&action=exportmodel&includeweights=0"),void(window.location.href=i.attr("href"));var j=b.get_strings([{key:"export",component:"tool_analytics"}]),k=e.create({type:e.types.SAVE_CANCEL}),l=g.render("tool_analytics/export_options",{});a.when(j,k).then(function(b,c){return c.getRoot().on(f.hidden,c.destroy.bind(c)),c.setTitle(b[0]),c.setSaveButtonText(b[0]),c.setBody(l),c.getRoot().on(f.save,function(){var b=a("input[name='exportoption']:checked").val();"exportdata"==b?i.attr("href",i.attr("href")+"&action=exportdata"):(i.attr("href",i.attr("href")+"&action=exportmodel"),a("#id-includeweights").is(":checked")?i.attr("href",i.attr("href")+"&includeweights=1"):i.attr("href",i.attr("href")+"&includeweights=0")),window.location.href=i.attr("href")}),c.show(),c}).fail(d.exception)})}}});
\ No newline at end of file
diff --git a/admin/tool/analytics/amd/src/model.js b/admin/tool/analytics/amd/src/model.js
index f39c5776e5e12..8d35e8648b7d9 100644
--- a/admin/tool/analytics/amd/src/model.js
+++ b/admin/tool/analytics/amd/src/model.js
@@ -149,6 +149,71 @@ define(['jquery', 'core/str', 'core/log', 'core/notification', 'core/modal_facto
return;
});
+ modal.show();
+ return modal;
+ }).fail(Notification.exception);
+ });
+ },
+
+ /**
+ * Displays export options.
+ *
+ * We have two main options: export training data and export configuration.
+ * The 2nd option has an extra option: include the trained algorithm weights.
+ *
+ * @param {String} actionId
+ * @param {Boolean} isTrained
+ */
+ selectExportOptions: function(actionId, isTrained) {
+ $('[data-action-id="' + actionId + '"]').on('click', function(ev) {
+ ev.preventDefault();
+
+ var a = $(ev.currentTarget);
+
+ if (!isTrained) {
+ // Export the model configuration if the model is not trained. We can't export anything else.
+ a.attr('href', a.attr('href') + '&action=exportmodel&includeweights=0');
+ window.location.href = a.attr('href');
+ return;
+ }
+
+ var stringsPromise = Str.get_strings([
+ {
+ key: 'export',
+ component: 'tool_analytics'
+ }
+ ]);
+ var modalPromise = ModalFactory.create({type: ModalFactory.types.SAVE_CANCEL});
+ var bodyPromise = Templates.render('tool_analytics/export_options', {});
+
+ $.when(stringsPromise, modalPromise).then(function(strings, modal) {
+
+ modal.getRoot().on(ModalEvents.hidden, modal.destroy.bind(modal));
+
+ modal.setTitle(strings[0]);
+ modal.setSaveButtonText(strings[0]);
+ modal.setBody(bodyPromise);
+
+ modal.getRoot().on(ModalEvents.save, function() {
+
+ var exportOption = $("input[name='exportoption']:checked").val();
+
+ if (exportOption == 'exportdata') {
+ a.attr('href', a.attr('href') + '&action=exportdata');
+
+ } else {
+ a.attr('href', a.attr('href') + '&action=exportmodel');
+ if ($("#id-includeweights").is(':checked')) {
+ a.attr('href', a.attr('href') + '&includeweights=1');
+ } else {
+ a.attr('href', a.attr('href') + '&includeweights=0');
+ }
+ }
+
+ window.location.href = a.attr('href');
+ return;
+ });
+
modal.show();
return modal;
}).fail(Notification.exception);
diff --git a/admin/tool/analytics/classes/output/models_list.php b/admin/tool/analytics/classes/output/models_list.php
index 351dae4d01d48..5152b6d273f25 100644
--- a/admin/tool/analytics/classes/output/models_list.php
+++ b/admin/tool/analytics/classes/output/models_list.php
@@ -235,22 +235,27 @@ public function export_for_template(\renderer_base $output) {
$actionsmenu->add($icon);
}
- // Export training data.
- if (!$model->is_static() && $model->is_trained()) {
- $urlparams['action'] = 'exportdata';
- $url = new \moodle_url('model.php', $urlparams);
- $icon = new \action_menu_link_secondary($url, new \pix_icon('i/export',
- get_string('exporttrainingdata', 'tool_analytics')), get_string('exporttrainingdata', 'tool_analytics'));
- $actionsmenu->add($icon);
- }
+ // Export.
+ if (!$model->is_static()) {
- // Export model.
- if (!$model->is_static() && $model->get_indicators() && !empty($modeldata->timesplitting)) {
- $urlparams['action'] = 'exportmodel';
- $url = new \moodle_url('model.php', $urlparams);
- $icon = new \action_menu_link_secondary($url, new \pix_icon('i/backup',
- get_string('exportmodel', 'tool_analytics')), get_string('exportmodel', 'tool_analytics'));
- $actionsmenu->add($icon);
+ $fullysetup = $model->get_indicators() && !empty($modeldata->timesplitting);
+ $istrained = $model->is_trained();
+
+ if ($fullysetup || $istrained) {
+
+ $url = new \moodle_url('model.php', $urlparams);
+ // Clear the previous action param from the URL, we will set it in JS.
+ $url->remove_params('action');
+
+ $actionid = 'export-' . $model->get_id();
+ $PAGE->requires->js_call_amd('tool_analytics/model', 'selectExportOptions',
+ [$actionid, $istrained]);
+
+ $icon = new \action_menu_link_secondary($url, new \pix_icon('i/export',
+ get_string('export', 'tool_analytics')), get_string('export', 'tool_analytics'),
+ ['data-action-id' => $actionid]);
+ $actionsmenu->add($icon);
+ }
}
// Invalid analysables.
diff --git a/admin/tool/analytics/lang/en/tool_analytics.php b/admin/tool/analytics/lang/en/tool_analytics.php
index 354dc960b4ef8..dc0c5c90b6791 100644
--- a/admin/tool/analytics/lang/en/tool_analytics.php
+++ b/admin/tool/analytics/lang/en/tool_analytics.php
@@ -66,6 +66,8 @@
$string['evaluationmodecolconfiguration'] = 'Configuration';
$string['evaluationmodeconfiguration'] = 'Evaluate the model configuration';
$string['evaluationinbatches'] = 'The site contents are calculated and stored in batches. The evaluation process may be stopped at any time. The next time it is run, it will continue from the point when it was stopped.';
+$string['export'] = 'Export';
+$string['exportincludeweights'] = 'Include the weights of the trained model';
$string['exportmodel'] = 'Export configuration';
$string['exporttrainingdata'] = 'Export training data';
$string['getpredictionsresultscli'] = 'Results using {$a->name} (id: {$a->id}) time-splitting method';
diff --git a/admin/tool/analytics/model.php b/admin/tool/analytics/model.php
index a58b5d79bf538..3cca64ebda405 100644
--- a/admin/tool/analytics/model.php
+++ b/admin/tool/analytics/model.php
@@ -235,8 +235,11 @@
break;
case 'exportmodel':
+
+ $includeweights = optional_param('includeweights', 1, PARAM_INT);
+
$zipfilename = 'model-' . $model->get_unique_id() . '-' . microtime(false) . '.zip';
- $zipfilepath = $model->export_model($zipfilename);
+ $zipfilepath = $model->export_model($zipfilename, $includeweights);
send_temp_file($zipfilepath, $zipfilename);
break;
diff --git a/admin/tool/analytics/templates/export_options.mustache b/admin/tool/analytics/templates/export_options.mustache
new file mode 100644
index 0000000000000..b7180abc977f0
--- /dev/null
+++ b/admin/tool/analytics/templates/export_options.mustache
@@ -0,0 +1,57 @@
+{{!
+ 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