Merge branch 'MDL-59106_master' of git://github.com/markn86/moodle

This commit is contained in:
Jake Dallimore 2017-10-12 10:53:20 +08:00
commit 0ce9026307
14 changed files with 150 additions and 314 deletions

View File

@ -30,7 +30,7 @@ $string['bettercli'] = 'Evaluating models and generating predictions may involve
$string['cantguessstartdate'] = 'Can\'t guess the start date';
$string['cantguessenddate'] = 'Can\'t guess the end date';
$string['clienablemodel'] = 'You can enable the model by selecting a time-splitting method by its ID. Note that you can also enable it later using the web interface (\'none\' to exit).';
$string['clievaluationandpredictions'] = 'A cron task iterates through enabled models and gets predictions. Models evaluation via command line is disabled. You can allow these processes to be executed manually via web interface by enabling <a href="{$a}">\'onlycli\' analytics setting</a>';
$string['clievaluationandpredictions'] = 'A scheduled task iterates through enabled models and gets predictions. Models evaluation via the web interface is disabled. You can allow these processes to be executed manually via the web interface by disabling the <a href="{$a}">\'onlycli\'</a> analytics setting';
$string['editmodel'] = 'Edit "{$a}" model';
$string['edittrainedwarning'] = 'This model has already been trained. Note that changing its indicators or its time-splitting method will delete its previous predictions and start generating new predictions.';
$string['enabled'] = 'Enabled';

View File

@ -932,8 +932,6 @@ class model {
* @return \context
*/
protected function prepare_prediction_record($sampleid, $rangeindex, $prediction, $predictionscore, $calculations) {
global $DB;
$context = $this->get_analyser()->sample_access_context($sampleid);
$record = new \stdClass();
@ -946,6 +944,15 @@ class model {
$record->calculations = $calculations;
$record->timecreated = time();
$analysable = $this->get_analyser()->get_sample_analysable($sampleid);
$timesplitting = $this->get_time_splitting();
$timesplitting->set_analysable($analysable);
$range = $timesplitting->get_range_by_index($rangeindex);
if ($range) {
$record->timestart = $range['start'];
$record->timeend = $range['end'];
}
return array($record, $context);
}

View File

@ -3623,6 +3623,8 @@
<FIELD NAME="predictionscore" TYPE="number" LENGTH="10" NOTNULL="true" SEQUENCE="false" DECIMALS="5"/>
<FIELD NAME="calculations" TYPE="text" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/>
<FIELD NAME="timestart" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/>
<FIELD NAME="timeend" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>

View File

@ -2676,6 +2676,28 @@ function xmldb_main_upgrade($oldversion) {
// Main savepoint reached.
upgrade_main_savepoint(true, 2017101000.01);
}
if ($oldversion < 2017101000.02) {
// Define field 'timestart' to be added to 'analytics_predictions'.
$table = new xmldb_table('analytics_predictions');
$field = new xmldb_field('timestart', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'timecreated');
// Conditionally launch add field 'timestart'.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Define field 'timeend' to be added to 'analytics_predictions'.
$field = new xmldb_field('timeend', XMLDB_TYPE_INTEGER, '10', null, null, null, null, 'timestart');
// Conditionally launch add field 'timeend'.
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// Main savepoint reached.
upgrade_main_savepoint(true, 2017101000.02);
}
return true;

View File

@ -71,10 +71,23 @@ class insight implements \renderable, \templatable {
* @return \stdClass
*/
public function export_for_template(\renderer_base $output) {
// Get the prediction data.
$predictiondata = $this->prediction->get_prediction_data();
$data = new \stdClass();
$data->insightname = format_string($this->model->get_target()->get_name());
// Get the details.
$data->timecreated = userdate($predictiondata->timecreated);
$data->timerange = '';
if (!empty($predictiondata->timestart) && !empty($predictiondata->timeend)) {
$timerange = new \stdClass();
$timerange->timestart = userdate($predictiondata->timestart);
$timerange->timeend = userdate($predictiondata->timeend);
$data->timerange = get_string('timerangewithdata', 'report_insights', $timerange);
}
// Sample info (determined by the analyser).
list($data->sampledescription, $samplerenderable) = $this->model->prediction_sample_description($this->prediction);
@ -84,10 +97,10 @@ class insight implements \renderable, \templatable {
}
// Prediction info.
$predictedvalue = $this->prediction->get_prediction_data()->prediction;
$predictionid = $this->prediction->get_prediction_data()->id;
$predictedvalue = $predictiondata->prediction;
$predictionid = $predictiondata->id;
$data->predictiondisplayvalue = $this->model->get_target()->get_display_value($predictedvalue);
list($data->style, $data->outcomeicon) = $this->get_calculation_display($this->model->get_target(),
list($data->style, $data->outcomeicon) = self::get_calculation_display($this->model->get_target(),
floatval($predictedvalue), $output);
$actions = $this->model->get_target()->prediction_actions($this->prediction, $this->includedetailsaction);
@ -124,7 +137,7 @@ class insight implements \renderable, \templatable {
$obj = new \stdClass();
$obj->name = call_user_func(array($calculation->indicator, 'get_name'));
$obj->displayvalue = $calculation->indicator->get_display_value($calculation->value, $calculation->subtype);
list($obj->style, $obj->outcomeicon) = $this->get_calculation_display($calculation->indicator,
list($obj->style, $obj->outcomeicon) = self::get_calculation_display($calculation->indicator,
floatval($calculation->value), $output, $calculation->subtype);
$data->calculations[] = $obj;
@ -149,7 +162,7 @@ class insight implements \renderable, \templatable {
* @param string|false $subtype
* @return array The style as 'success', 'info', 'warning' or 'danger' and pix_icon
*/
protected function get_calculation_display(\core_analytics\calculable $calculable, $value, $output, $subtype = false) {
public static function get_calculation_display(\core_analytics\calculable $calculable, $value, $output, $subtype = false) {
$outcome = $calculable->get_calculation_outcome($value, $subtype);
switch ($outcome) {
case \core_analytics\calculable::OUTCOME_NEUTRAL:

View File

@ -95,17 +95,39 @@ class insights_list implements \renderable, \templatable {
if ($this->model->uses_insights()) {
$predictionsdata = $this->model->get_predictions($this->context, true, $this->page, $this->perpage);
$data->insights = array();
$data->predictions = array();
$predictionvalues = array();
$insights = array();
if ($predictionsdata) {
list($total, $predictions) = $predictionsdata;
foreach ($predictions as $prediction) {
$predictedvalue = $prediction->get_prediction_data()->prediction;
// Only need to fill this data once.
if (!isset($predictionvalues[$predictedvalue])) {
$preddata = array();
$preddata['predictiondisplayvalue'] = $this->model->get_target()->get_display_value($predictedvalue);
list($preddata['style'], $preddata['outcomeicon']) =
insight::get_calculation_display($this->model->get_target(), $predictedvalue, $output);
$predictionvalues[$predictedvalue] = $preddata;
}
$insightrenderable = new \report_insights\output\insight($prediction, $this->model, true);
$data->insights[] = $insightrenderable->export_for_template($output);
$insights[$predictedvalue][] = $insightrenderable->export_for_template($output);
}
// Ok, now we have all the data we want, put it into a format that mustache can handle.
foreach ($predictionvalues as $key => $prediction) {
if (isset($insights[$key])) {
$prediction['insights'] = $insights[$key];
}
$data->predictions[] = $prediction;
}
}
if (empty($data->insights) && $this->page == 0) {
if (empty($insights) && $this->page == 0) {
if ($this->model->any_prediction_obtained()) {
$data->noinsights = get_string('noinsights', 'analytics');
} else {

View File

@ -40,4 +40,7 @@ $string['prediction'] = 'Prediction';
$string['predictioncalculations'] = 'Indicator calculations';
$string['predictiondetails'] = 'Prediction details';
$string['nodetailsavailable'] = 'No prediction details are relevant.';
$string['timecreated'] = 'Time predicted';
$string['timerange'] = 'Time range';
$string['timerangewithdata'] = '{$a->timestart} to {$a->timeend}';
$string['selectotherinsights'] = 'Select other insights...';

View File

@ -31,29 +31,16 @@
Example context (json):
{
"sampleimage": "<a href=\"#\">Link</a>",
"sampledescription": "Sample description",
"style": "success",
"outcomeicon": {
"attributes": [
{"name": "src", "value": "https://moodle.org/logo/moodle-logo.svg" }
]
},
"predictiondisplayvalue": "This dev will understand it"
"sampledescription": "Sample description"
}
}}
<tr>
<td class="col-sm-6">
<td class="col-sm-10">
{{#sampleimage}}
{{{sampleimage}}}
{{/sampleimage}}
{{{sampledescription}}}
</td>
<td class="{{#style}}table-{{style}}{{/style}} col-sm-4">
{{#outcomeicon}}
{{> core/pix_icon}}
{{/outcomeicon}}
<span>{{predictiondisplayvalue}}</span>
</td>
<td class="col-sm-2">
{{#actions}}
{{> core/action_menu}}

View File

@ -31,6 +31,8 @@
Example context (json):
{
"insightname": "Best insight ever",
"timecreated": "Thursday, 5 October 2017, 4:16 PM",
"timerange": "Monday, 4 September 2017, 6:00 PM to Thursday, 5 October 2017, 12:00 AM",
"sampleimage": "<a href=\"#\">Link</a>",
"sampledescription": "Sample description",
"style": "success",
@ -73,11 +75,18 @@
<h2 class="m-b-2">{{#str}}insightprediction, report_insights, {{insightname}} {{/str}}</h2>
<table class="generaltable insights-list">
<caption>{{#str}}insight, report_insights{{/str}}</caption>
<caption>
{{#str}}prediction, report_insights{{/str}}:
<span class="{{#style}}table-{{style}}{{/style}}">
{{#outcomeicon}}
{{> core/pix_icon}}
{{/outcomeicon}}
{{predictiondisplayvalue}}
</span>
</caption>
<thead>
<tr>
<th scope="col" class="col-sm-6">{{#str}}name{{/str}}</th>
<th scope="col" class="col-sm-4">{{#str}}prediction, report_insights{{/str}}</th>
<th scope="col" class="col-sm-10">{{#str}}name{{/str}}</th>
<th scope="col" class="col-sm-2">{{#str}}actions{{/str}}</th>
</tr>
</thead>
@ -86,8 +95,23 @@
</tbody>
</table>
<table class="generaltable prediction-calculations">
<table class="generaltable prediction-timedetails">
<caption>{{#str}}predictiondetails, report_insights{{/str}}</caption>
<tbody>
<tr>
<td scope="col" class="col-sm-3">{{#str}}timecreated, report_insights{{/str}}</td>
<td scope="col" class="col-sm-9">{{timecreated}}</td>
</tr>
{{#timerange}}
<tr>
<td scope="col" class="col-sm-3">{{#str}}timerange, report_insights{{/str}}</td>
<td scope="col" class="col-sm-9">{{.}}</td>
</tr>
{{/timerange}}
</tbody>
</table>
<table class="generaltable prediction-calculations">
<caption class="accesshide">{{#str}}predictioncalculations, report_insights{{/str}}</caption>
<thead>
<tr>
<th scope="col" class="col-sm-8">{{#str}}indicator, report_insights{{/str}}</th>
@ -99,7 +123,7 @@
<tr>
<td class="{{#style}}table-{{style}}{{/style}} col-sm-8">{{name}}</td>
<td class="{{#style}}table-{{style}}{{/style}} col-sm-4">{{#outcomeicon}}{{> core/pix_icon}}{{/outcomeicon}} {{displayvalue}}</td>
</td>
</tr>
{{/calculations}}
</tbody>
</table>

View File

@ -31,27 +31,35 @@
Example context (json):
{
"insightname": "Best insight ever",
"insights": [
"predictions": [
{
"sampleimage": "<a href=\"#\">Link</a>",
"sampledescription": "Sample description",
"predictiondisplayvalue": "This dev will understand it",
"style": "success",
"outcomeicon": {
"attributes": [
{"name": "src", "value": "https://moodle.org/logo/moodle-logo.svg" }
]
},
"predictiondisplayvalue": "This dev will understand it"
"insights": [
{
"sampleimage": "<a href=\"#\">Link</a>",
"sampledescription": "Sample description"
}
]
}, {
"sampleimage": "<a href=\"#\">Any renderable</a>",
"sampledescription": "Another sample description",
"predictiondisplayvalue": "This dev will not understand it",
"style": "danger",
"outcomeicon": {
"attributes": [
{"name": "src", "value": "https://moodle.org/logo/moodle-logo.svg" }
]
},
"predictiondisplayvalue": "This dev will not understand it"
"insights": [
{
"sampleimage": "<a href=\"#\">Any renderable</a>",
"sampledescription": "Another sample description"
}
]
}
],
"noinsights": false
@ -67,21 +75,30 @@
<h2 class="m-b-2">{{{insightname}}}</h2>
{{^noinsights}}
{{{ pagingbar }}}
<table class="generaltable insights-list">
<caption>{{#str}}insights, report_insights{{/str}}</caption>
<thead>
<tr>
<th scope="col" class="col-sm-6">{{#str}}name{{/str}}</th>
<th scope="col" class="col-sm-4">{{#str}}prediction, report_insights{{/str}}</th>
<th scope="col" class="col-sm-2">{{#str}}actions{{/str}}</th>
</tr>
</thead>
<tbody>
{{#insights}}
{{#predictions}}
<table class="generaltable insights-list">
<caption>
{{#str}}prediction, report_insights{{/str}}:
<span class="{{#style}}table-{{style}}{{/style}}">
{{#outcomeicon}}
{{> core/pix_icon}}
{{/outcomeicon}}
{{predictiondisplayvalue}}
</span>
</caption>
<thead>
<tr>
<th scope="col" class="col-sm-10">{{#str}}name{{/str}}</th>
<th scope="col" class="col-sm-2">{{#str}}actions{{/str}}</th>
</tr>
</thead>
{{#insights}}
<tbody>
{{> report_insights/insight}}
{{/insights}}
</tbody>
</table>
</tbody>
{{/insights}}
</table>
{{/predictions}}
{{{ pagingbar }}}
{{/noinsights}}
{{#noinsights}}

View File

@ -1,62 +0,0 @@
{{!
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/>.
}}
{{!
@template report_insights/insight
Template for a insight.
Classes required for JS:
* none
Data attributes required for JS:
* none
Context variables required for this template:
* none
Example context (json):
{
"sampleimage": "<a href=\"#\">Link</a>",
"sampledescription": "Sample description",
"style": "success",
"outcomeicon": {
"attributes": [
{"name": "src", "value": "https://moodle.org/logo/moodle-logo.svg" }
]
},
"predictiondisplayvalue": "This dev will understand it"
}
}}
<tr>
<td class="col-sm-6">
{{#sampleimage}}
{{{sampleimage}}}
{{/sampleimage}}
{{{sampledescription}}}
</td>
<td class="col-sm-4">
{{#outcomeicon}}
{{> core/pix_icon}}
{{/outcomeicon}}
<span>{{predictiondisplayvalue}}</span>
</td>
<td class="col-sm-2">
{{#actions}}
{{> core/action_menu}}
{{/actions}}
</td>
</tr>

View File

@ -1,108 +0,0 @@
{{!
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/>.
}}
{{!
@template report_insights/insight_details
Actions panel at the bottom of the assignment grading UI.
Classes required for JS:
* none
Data attributes required for JS:
* none
Context variables required for this template:
* none
Example context (json):
{
"insightname": "Best insight ever",
"sampleimage": "<a href=\"#\">Link</a>",
"sampledescription": "Sample description",
"style": "success",
"outcomeicon": {
"attributes": [
{"name": "src", "value": "https://moodle.org/logo/moodle-logo.svg" }
]
},
"predictiondisplayvalue": "This dev will understand it",
"calculations": [
{
"style": "success",
"outcomeicon": {
"attributes": [
{"name": "src", "value": "https://moodle.org/logo/moodle-logo.svg" }
]
},
"name": "Indicator 1",
"displayvalue": "yes"
}, {
"style": "info",
"name": "Indicator 2",
"displayvalue": "20%"
}, {
"style": "",
"name": "Indicator 2",
"displayvalue": "20%"
}, {
"style": "warning",
"name": "Indicator 2",
"displayvalue": "20%"
}, {
"style": "danger",
"name": "Indicator 2",
"displayvalue": "20%"
}
]
}
}}
<h2 class="m-b-2">{{#str}}insightprediction, report_insights, {{insightname}} {{/str}}</h2>
<table class="generaltable insights-list">
<caption>{{#str}}insight, report_insights{{/str}}</caption>
<thead>
<tr>
<th scope="col" class="col-sm-6">{{#str}}name{{/str}}</th>
<th scope="col" class="col-sm-4">{{#str}}prediction, report_insights{{/str}}</th>
<th scope="col" class="col-sm-2">{{#str}}actions{{/str}}</th>
</tr>
</thead>
<tbody>
{{> report_insights/insight}}
</tbody>
</table>
<table class="generaltable prediction-calculations">
<caption>{{#str}}predictiondetails, report_insights{{/str}}</caption>
<thead>
<tr>
<th scope="col" class="col-sm-8">{{#str}}indicator, report_insights{{/str}}</th>
<th scope="col" class="col-sm-4">{{#str}}calculatedvalue, report_insights{{/str}}</th>
</tr>
</thead>
<tbody>
{{#calculations}}
<tr>
<td class="col-sm-8">{{name}}</td>
<td class="col-sm-4">{{#outcomeicon}}{{> core/pix_icon}}{{/outcomeicon}} {{displayvalue}}</td>
</td>
{{/calculations}}
</tbody>
</table>
{{#nocalculations}}
{{> core/notification_info}}
{{/nocalculations}}

View File

@ -1,91 +0,0 @@
{{!
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/>.
}}
{{!
@template report_insights/insights_list
Template for the insights list.
Classes required for JS:
* none
Data attributes required for JS:
* none
Context variables required for this template:
* none
Example context (json):
{
"insightname": "Best insight ever",
"insights": [
{
"sampleimage": "<a href=\"#\">Link</a>",
"sampledescription": "Sample description",
"style": "success",
"outcomeicon": {
"attributes": [
{"name": "src", "value": "https://moodle.org/logo/moodle-logo.svg" }
]
},
"predictiondisplayvalue": "This dev will understand it"
}, {
"sampleimage": "<a href=\"#\">Any renderable</a>",
"sampledescription": "Another sample description",
"style": "danger",
"outcomeicon": {
"attributes": [
{"name": "src", "value": "https://moodle.org/logo/moodle-logo.svg" }
]
},
"predictiondisplayvalue": "This dev will not understand it"
}
],
"noinsights": false
}
}}
{{#modelselector}}
<div class="m-b-2">
{{> core/single_select}}
</div>
{{/modelselector}}
<h2 class="m-b-2">{{{insightname}}}</h2>
{{^noinsights}}
{{{ pagingbar }}}
<table class="generaltable insights-list">
<caption>{{#str}}insights, report_insights{{/str}}</caption>
<thead>
<tr>
<th scope="col" class="col-sm-6">{{#str}}name{{/str}}</th>
<th scope="col" class="col-sm-4">{{#str}}prediction, report_insights{{/str}}</th>
<th scope="col" class="col-sm-2">{{#str}}actions{{/str}}</th>
</tr>
</thead>
<tbody>
{{#insights}}
{{> report_insights/insight}}
{{/insights}}
</tbody>
</table>
{{{ pagingbar }}}
{{/noinsights}}
{{#noinsights}}
<div class="m-t-2">
{{> core/notification_info}}
</div>
{{/noinsights}}

View File

@ -29,7 +29,7 @@
defined('MOODLE_INTERNAL') || die();
$version = 2017101000.01; // YYYYMMDD = weekly release date of this DEV branch.
$version = 2017101000.02; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.