Merge branch 'MDL-59153_master' of git://github.com/dmonllao/moodle

This commit is contained in:
Dan Poltawski 2017-09-04 10:03:04 +01:00 committed by David Monllao
commit 3c4675a002
19 changed files with 606 additions and 87 deletions

View File

@ -115,7 +115,9 @@ class models_list implements \renderable, \templatable {
} }
// Model predictions list. // Model predictions list.
if ($model->uses_insights()) { if (!$model->is_enabled()) {
$modeldata->noinsights = get_string('disabledmodel', 'analytics');
} else if ($model->uses_insights()) {
$predictioncontexts = $model->get_predictions_contexts(); $predictioncontexts = $model->get_predictions_contexts();
if ($predictioncontexts) { if ($predictioncontexts) {

View File

@ -443,7 +443,12 @@ class model {
// Reset trained flag. // Reset trained flag.
$this->model->trained = 0; $this->model->trained = 0;
} else if ($this->model->enabled != $enabled) {
// We purge the cached contexts with insights as some will not be visible anymore.
$this->purge_insights_cache();
} }
$this->model->enabled = intval($enabled); $this->model->enabled = intval($enabled);
$this->model->indicators = $indicatorsstr; $this->model->indicators = $indicatorsstr;
$this->model->timesplitting = $timesplittingid; $this->model->timesplitting = $timesplittingid;
@ -971,6 +976,13 @@ class model {
$this->model->timesplitting = $timesplittingid; $this->model->timesplitting = $timesplittingid;
$this->model->version = $now; $this->model->version = $now;
} }
// Purge pages with insights as this may change things.
if ($timesplittingid && $timesplittingid !== $this->model->timesplitting ||
$this->model->enabled != 1) {
$this->purge_insights_cache();
}
$this->model->enabled = 1; $this->model->enabled = 1;
$this->model->timemodified = $now; $this->model->timemodified = $now;
@ -1375,6 +1387,13 @@ class model {
// We don't expect people to clear models regularly and the cost of filling the cache is // We don't expect people to clear models regularly and the cost of filling the cache is
// 1 db read per context. // 1 db read per context.
$this->purge_insights_cache();
}
/**
* Purges the insights cache.
*/
private function purge_insights_cache() {
$cache = \cache::make('core', 'contextwithinsights'); $cache = \cache::make('core', 'contextwithinsights');
$cache->purge(); $cache->purge();
} }

View File

@ -30,6 +30,7 @@ $string['analyticslogstore_help'] = 'The log store that will be used by the anal
$string['analyticssettings'] = 'Analytics settings'; $string['analyticssettings'] = 'Analytics settings';
$string['coursetoolong'] = 'The course is too long'; $string['coursetoolong'] = 'The course is too long';
$string['enabledtimesplittings'] = 'Time splitting methods'; $string['enabledtimesplittings'] = 'Time splitting methods';
$string['disabledmodel'] = 'Disabled model';
$string['erroralreadypredict'] = '{$a} file has already been used to predict'; $string['erroralreadypredict'] = '{$a} file has already been used to predict';
$string['errorcannotreaddataset'] = 'Dataset file {$a} can not be read'; $string['errorcannotreaddataset'] = 'Dataset file {$a} can not be read';
$string['errorcannotwritedataset'] = 'Dataset file {$a} can not be written'; $string['errorcannotwritedataset'] = 'Dataset file {$a} can not be written';
@ -53,7 +54,6 @@ $string['errorunexistingmodel'] = 'Unexisting model {$a}';
$string['errorunknownaction'] = 'Unknown action'; $string['errorunknownaction'] = 'Unknown action';
$string['eventpredictionactionstarted'] = 'Prediction action started'; $string['eventpredictionactionstarted'] = 'Prediction action started';
$string['insightmessagesubject'] = 'New insight for "{$a->contextname}": {$a->insightname}'; $string['insightmessagesubject'] = 'New insight for "{$a->contextname}": {$a->insightname}';
$string['insightinfo'] = '{$a->insightname} - {$a->contextname}';
$string['insightinfomessage'] = 'The system generated some insights for you: {$a}'; $string['insightinfomessage'] = 'The system generated some insights for you: {$a}';
$string['insightinfomessagehtml'] = 'The system generated some insights for you: <a href="{$a}">{$a}</a>.'; $string['insightinfomessagehtml'] = 'The system generated some insights for you: <a href="{$a}">{$a}</a>.';
$string['invalidtimesplitting'] = 'Model with id {$a} needs a time splitting method before it can be used to train'; $string['invalidtimesplitting'] = 'Model with id {$a} needs a time splitting method before it can be used to train';

View File

@ -45,7 +45,7 @@ if (!isset($actions[$actionname])) {
} }
$modelready = $model->is_enabled() && $model->is_trained() && $model->predictions_exist($context); $modelready = $model->is_enabled() && $model->is_trained() && $model->predictions_exist($context);
if (!$modelready && !has_capability('moodle/analytics:managemodels', $context)) { if (!$modelready) {
$PAGE->set_pagelayout('report'); $PAGE->set_pagelayout('report');
@ -53,7 +53,7 @@ if (!$modelready && !has_capability('moodle/analytics:managemodels', $context))
$PAGE->set_title($context->get_context_name()); $PAGE->set_title($context->get_context_name());
$PAGE->set_heading($context->get_context_name()); $PAGE->set_heading($context->get_context_name());
echo $OUTPUT->header(); echo $OUTPUT->header();
echo $OUTPUT->notification(get_string('disabledmodel', 'analytics'), \core\output\notification::NOTIFY_INFO); echo $OUTPUT->notification(get_string('disabledmodel', 'report_insights'), \core\output\notification::NOTIFY_INFO);
echo $OUTPUT->footer(); echo $OUTPUT->footer();
exit(0); exit(0);
} }

View File

@ -73,20 +73,22 @@ class insight implements \renderable, \templatable {
public function export_for_template(\renderer_base $output) { public function export_for_template(\renderer_base $output) {
$data = new \stdClass(); $data = new \stdClass();
$data->insightname = format_string($this->model->get_target()->get_name());
// Sample info (determined by the analyser). // Sample info (determined by the analyser).
list($data->sampledescription, $samplerenderable) = $this->model->prediction_sample_description($this->prediction); list($data->sampledescription, $samplerenderable) = $this->model->prediction_sample_description($this->prediction);
// Sampleimage is a renderable we should pass it to HTML. // Sampleimage is a renderable we should pass it to HTML.
if ($samplerenderable) { if ($samplerenderable) {
$data->samplelink = $output->render($samplerenderable); $data->sampleimage = $output->render($samplerenderable);
} }
// Prediction info. // Prediction info.
$predictedvalue = $this->prediction->get_prediction_data()->prediction; $predictedvalue = $this->prediction->get_prediction_data()->prediction;
$predictionid = $this->prediction->get_prediction_data()->id; $predictionid = $this->prediction->get_prediction_data()->id;
$data->predictiondisplayvalue = $this->model->get_target()->get_display_value($predictedvalue); $data->predictiondisplayvalue = $this->model->get_target()->get_display_value($predictedvalue);
$data->predictionstyle = $this->get_calculation_style($this->model->get_target(), $predictedvalue); list($data->style, $data->outcomeicon) = $this->get_calculation_display($this->model->get_target(), $predictedvalue,
$output);
$actions = $this->model->get_target()->prediction_actions($this->prediction, $this->includedetailsaction); $actions = $this->model->get_target()->prediction_actions($this->prediction, $this->includedetailsaction);
if ($actions) { if ($actions) {
@ -122,39 +124,58 @@ class insight implements \renderable, \templatable {
$obj = new \stdClass(); $obj = new \stdClass();
$obj->name = call_user_func(array($calculation->indicator, 'get_name')); $obj->name = call_user_func(array($calculation->indicator, 'get_name'));
$obj->displayvalue = $calculation->indicator->get_display_value($calculation->value, $calculation->subtype); $obj->displayvalue = $calculation->indicator->get_display_value($calculation->value, $calculation->subtype);
$obj->style = $this->get_calculation_style($calculation->indicator, $calculation->value, $calculation->subtype); list($obj->style, $obj->outcomeicon) = $this->get_calculation_display($calculation->indicator, $calculation->value,
$output, $calculation->subtype);
$data->calculations[] = $obj; $data->calculations[] = $obj;
} }
if (empty($data->calculations)) {
$data->nocalculations = (object)array(
'message' => get_string('nodetailsavailable', 'report_insights'),
'closebutton' => false
);
}
return $data; return $data;
} }
/** /**
* Returns a CSS class from the calculated value outcome. * Returns display info for the calculated value outcome.
* *
* @param \core_analytics\calculable $calculable * @param \core_analytics\calculable $calculable
* @param float $value * @param float $value
* @param \renderer_base $output
* @param string|false $subtype * @param string|false $subtype
* @return string * @return array The style as 'success', 'info', 'warning' or 'danger' and pix_icon
*/ */
protected function get_calculation_style(\core_analytics\calculable $calculable, $value, $subtype = false) { protected function get_calculation_display(\core_analytics\calculable $calculable, $value, $output, $subtype = false) {
$outcome = $calculable->get_calculation_outcome($value, $subtype); $outcome = $calculable->get_calculation_outcome($value, $subtype);
switch ($outcome) { switch ($outcome) {
case \core_analytics\calculable::OUTCOME_NEUTRAL: case \core_analytics\calculable::OUTCOME_NEUTRAL:
$style = ''; $style = '';
$text = get_string('outcomeneutral', 'report_insights');
$icon = 't/check';
break; break;
case \core_analytics\calculable::OUTCOME_VERY_POSITIVE: case \core_analytics\calculable::OUTCOME_VERY_POSITIVE:
$style = 'alert alert-success'; $style = 'success';
$text = get_string('outcomeverypositive', 'report_insights');
$icon = 't/approve';
break; break;
case \core_analytics\calculable::OUTCOME_OK: case \core_analytics\calculable::OUTCOME_OK:
$style = 'alert alert-info'; $style = 'info';
$text = get_string('outcomeok', 'report_insights');
$icon = 't/check';
break; break;
case \core_analytics\calculable::OUTCOME_NEGATIVE: case \core_analytics\calculable::OUTCOME_NEGATIVE:
$style = 'alert alert-warning'; $style = 'warning';
$text = get_string('outcomenegative', 'report_insights');
$icon = 'i/warning';
break; break;
case \core_analytics\calculable::OUTCOME_VERY_NEGATIVE: case \core_analytics\calculable::OUTCOME_VERY_NEGATIVE:
$style = 'alert alert-danger'; $style = 'danger';
$text = get_string('outcomeverynegative', 'report_insights');
$icon = 'i/warning';
break; break;
default: default:
throw new \coding_exception('The outcome returned by ' . get_class($calculable) . '::get_calculation_outcome is ' . throw new \coding_exception('The outcome returned by ' . get_class($calculable) . '::get_calculation_outcome is ' .
@ -162,6 +183,7 @@ class insight implements \renderable, \templatable {
'\core_analytics\calculable::OUTCOME_OK, \core_analytics\calculable::OUTCOME_NEGATIVE, ' . '\core_analytics\calculable::OUTCOME_OK, \core_analytics\calculable::OUTCOME_NEGATIVE, ' .
'\core_analytics\calculable::OUTCOME_VERY_NEGATIVE or \core_analytics\calculable::OUTCOME_NEUTRAL'); '\core_analytics\calculable::OUTCOME_VERY_NEGATIVE or \core_analytics\calculable::OUTCOME_NEUTRAL');
} }
return $style; $icon = new \pix_icon($icon, $text);
return array($style, $icon->export_for_template($output));
} }
} }

View File

@ -88,6 +88,8 @@ class insights_list implements \renderable, \templatable {
global $PAGE; global $PAGE;
$data = new \stdClass(); $data = new \stdClass();
$data->insightname = format_string($this->model->get_target()->get_name());
$total = 0; $total = 0;
if ($this->model->uses_insights()) { if ($this->model->uses_insights()) {

View File

@ -75,7 +75,7 @@ class renderer extends plugin_renderer_base {
$PAGE->set_heading($insightinfo->contextname); $PAGE->set_heading($insightinfo->contextname);
$output = $OUTPUT->header(); $output = $OUTPUT->header();
$output .= $OUTPUT->notification(get_string('disabledmodel', 'analytics'), \core\output\notification::NOTIFY_INFO); $output .= $OUTPUT->notification(get_string('disabledmodel', 'report_insights'), \core\output\notification::NOTIFY_INFO);
$output .= $OUTPUT->footer(); $output .= $OUTPUT->footer();
return $output; return $output;

View File

@ -23,6 +23,7 @@
*/ */
require_once(__DIR__ . '/../../config.php'); require_once(__DIR__ . '/../../config.php');
require_once($CFG->libdir . '/adminlib.php');
$contextid = required_param('contextid', PARAM_INT); $contextid = required_param('contextid', PARAM_INT);
$modelid = optional_param('modelid', false, PARAM_INT); $modelid = optional_param('modelid', false, PARAM_INT);
@ -52,8 +53,13 @@ if ($modelid) {
unset($othermodels[$modelid]); unset($othermodels[$modelid]);
} }
// The URL in navigation only contains the contextid.
$params = array('contextid' => $contextid); $params = array('contextid' => $contextid);
$url = new \moodle_url('/report/insights/insights.php', $params); $navurl = new \moodle_url('/report/insights/insights.php', $params);
// This is the real page url, we need it to include the modelid so pagination and
// other stuff works as expected.
$url = clone $navurl;
if ($modelid) { if ($modelid) {
$url->param('modelid', $modelid); $url->param('modelid', $modelid);
} }
@ -61,6 +67,18 @@ if ($modelid) {
$PAGE->set_url($url); $PAGE->set_url($url);
$PAGE->set_pagelayout('report'); $PAGE->set_pagelayout('report');
if ($context->contextlevel === CONTEXT_SYSTEM) {
admin_externalpage_setup('reportinsights', '', null, '', array('pagelayout' => 'report'));
} else if ($context->contextlevel === CONTEXT_USER) {
$user = \core_user::get_user($context->instanceid, '*', MUST_EXIST);
$PAGE->navigation->extend_for_user($user);
$PAGE->add_report_nodes($user->id, array(
'name' => get_string('insights', 'report_insights'),
'url' => $url
));
}
$PAGE->navigation->override_active_url($navurl);
$renderer = $PAGE->get_renderer('report_insights'); $renderer = $PAGE->get_renderer('report_insights');
// No models with insights available at this context level. // No models with insights available at this context level.
@ -74,9 +92,8 @@ $model = new \core_analytics\model($modelid);
$insightinfo = new stdClass(); $insightinfo = new stdClass();
$insightinfo->contextname = $context->get_context_name(); $insightinfo->contextname = $context->get_context_name();
$insightinfo->insightname = $model->get_target()->get_name(); $insightinfo->insightname = $model->get_target()->get_name();
$title = get_string('insightinfo', 'analytics', $insightinfo);
if (!$model->is_enabled() && !has_capability('moodle/analytics:managemodels', $context)) { if (!$model->is_enabled()) {
echo $renderer->render_model_disabled($insightinfo); echo $renderer->render_model_disabled($insightinfo);
exit(0); exit(0);
} }
@ -86,8 +103,8 @@ if (!$model->uses_insights()) {
exit(0); exit(0);
} }
$PAGE->set_title($title); $PAGE->set_title($insightinfo->insightname);
$PAGE->set_heading($title); $PAGE->set_heading($insightinfo->contextname);
echo $OUTPUT->header(); echo $OUTPUT->header();

View File

@ -22,10 +22,22 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/ */
$string['calculatedvalue'] = 'Calculated value';
$string['disabledmodel'] = 'Sorry, this model has been disabled by the administrator'; $string['disabledmodel'] = 'Sorry, this model has been disabled by the administrator';
$string['indicator'] = 'Indicator';
$string['insightprediction'] = '{$a} prediction';
$string['insight'] = 'Insight';
$string['insights'] = 'Insights'; $string['insights'] = 'Insights';
$string['outcome'] = 'Outcome';
$string['outcomenegative'] = 'Negative outcome';
$string['outcomeneutral'] = 'Neutral outcome';
$string['outcomeok'] = 'Ok outcome';
$string['outcomepositive'] = 'Positive outcome';
$string['outcomeverypositive'] = 'Very positive outcome';
$string['outcomeverynegative'] = 'Very negative outcome';
$string['pluginname'] = 'Insights'; $string['pluginname'] = 'Insights';
$string['prediction'] = 'Prediction'; $string['prediction'] = 'Prediction';
$string['predictioncalculations'] = 'Indicator calculations';
$string['predictiondetails'] = 'Prediction details'; $string['predictiondetails'] = 'Prediction details';
$string['nodetailsavailable'] = 'No prediction details are relevant.';
$string['selectotherinsights'] = 'Select other insights...'; $string['selectotherinsights'] = 'Select other insights...';

View File

@ -36,20 +36,85 @@ function report_insights_extend_navigation_course($navigation, $course, $context
if (has_capability('moodle/analytics:listinsights', $context)) { if (has_capability('moodle/analytics:listinsights', $context)) {
$cache = \cache::make('core', 'contextwithinsights'); $modelids = report_insights_context_insights($context);
$modelids = $cache->get($context->id);
if ($modelids === false) {
// They will be full unless a model has been cleared.
$models = \core_analytics\manager::get_models_with_insights($context);
$modelids = array_keys($models);
$cache->set($context->id, $modelids);
}
if (!empty($modelids)) { if (!empty($modelids)) {
$url = new moodle_url('/report/insights/insights.php', array('contextid' => $context->id)); $url = new moodle_url('/report/insights/insights.php', array('contextid' => $context->id));
$settingsnode = navigation_node::create(get_string('insights', 'report_insights'), $url, navigation_node::TYPE_SETTING, $node = navigation_node::create(get_string('insights', 'report_insights'), $url, navigation_node::TYPE_SETTING,
null, null, new pix_icon('i/settings', '')); null, null, new pix_icon('i/report', get_string('insights', 'report_insights')));
$navigation->add_node($settingsnode); $navigation->add_node($node);
} }
} }
} }
/**
* Add nodes to myprofile page.
*
* @param \core_user\output\myprofile\tree $tree Tree object
* @param stdClass $user user object
* @param bool $iscurrentuser
* @param stdClass $course Course object
*
* @return bool
*/
function report_insights_myprofile_navigation(core_user\output\myprofile\tree $tree, $user, $iscurrentuser, $course) {
$context = \context_user::instance($user->id);
if (has_capability('moodle/analytics:listinsights', $context)) {
$modelids = report_insights_context_insights($context);
if (!empty($modelids)) {
$url = new moodle_url('/report/insights/insights.php', array('contextid' => $context->id));
$node = new core_user\output\myprofile\node('reports', 'insights', get_string('insights', 'report_insights'),
null, $url);
$tree->add_node($node);
}
}
}
/**
* Adds nodes to category navigation
*
* @param navigation_node $navigation The navigation node to extend
* @param context $context The context of the course
* @return void|null return null if we don't want to display the node.
*/
function report_insights_extend_navigation_category_settings($navigation, $context) {
if (has_capability('moodle/analytics:listinsights', $context)) {
$modelids = report_insights_context_insights($context);
if (!empty($modelids)) {
$url = new moodle_url('/report/insights/insights.php', array('contextid' => $context->id));
$node = navigation_node::create(
get_string('insights', 'report_insights'),
$url,
navigation_node::NODETYPE_LEAF,
null,
'insights',
new pix_icon('i/report', get_string('insights', 'report_insights'))
);
$navigation->add_node($node);
}
}
}
/**
* Returns the models that generated insights in the provided context.
*
* @param \context $context
* @return int[]
*/
function report_insights_context_insights(\context $context) {
$cache = \cache::make('core', 'contextwithinsights');
$modelids = $cache->get($context->id);
if ($modelids === false) {
// They will be full unless a model has been cleared.
$models = \core_analytics\manager::get_models_with_insights($context);
$modelids = array_keys($models);
$cache->set($context->id, $modelids);
}
return $modelids;
}

View File

@ -23,6 +23,7 @@
*/ */
require_once(__DIR__ . '/../../config.php'); require_once(__DIR__ . '/../../config.php');
require_once($CFG->libdir . '/adminlib.php');
$predictionid = required_param('id', PARAM_INT); $predictionid = required_param('id', PARAM_INT);
@ -37,15 +38,30 @@ $url = new \moodle_url('/report/insights/prediction.php', $params);
$PAGE->set_url($url); $PAGE->set_url($url);
$PAGE->set_pagelayout('report'); $PAGE->set_pagelayout('report');
$navurl = new \moodle_url('/report/insights/insights.php', array('contextid' => $context->id));
if ($context->contextlevel === CONTEXT_SYSTEM) {
admin_externalpage_setup('reportinsights', '', null, '', array('pagelayout' => 'report'));
} else if ($context->contextlevel === CONTEXT_USER) {
$user = \core_user::get_user($context->instanceid, '*', MUST_EXIST);
$PAGE->navigation->extend_for_user($user);
$modelinsightsurl = clone $navurl;
$modelinsightsurl->param('modelid', $model->get_id());
$PAGE->add_report_nodes($user->id, array(
'name' => get_string('insights', 'report_insights'),
'url' => $url
));
}
$PAGE->navigation->override_active_url($navurl);
$renderer = $PAGE->get_renderer('report_insights'); $renderer = $PAGE->get_renderer('report_insights');
$insightinfo = new stdClass(); $insightinfo = new stdClass();
$insightinfo->contextname = $context->get_context_name(); $insightinfo->contextname = $context->get_context_name();
$insightinfo->insightname = $model->get_target()->get_name(); $insightinfo->insightname = $model->get_target()->get_name();
$title = get_string('insightinfo', 'analytics', $insightinfo);
$modelready = $model->is_enabled() && $model->is_trained() && $model->predictions_exist($context); $modelready = $model->is_enabled() && $model->is_trained() && $model->predictions_exist($context);
if (!$modelready && !has_capability('moodle/analytics:managemodels', $context)) { if (!$modelready) {
echo $renderer->render_model_disabled($insightinfo); echo $renderer->render_model_disabled($insightinfo);
exit(0); exit(0);
} }
@ -55,8 +71,8 @@ if (!$model->uses_insights()) {
exit(0); exit(0);
} }
$PAGE->set_title($title); $PAGE->set_title($insightinfo->insightname);
$PAGE->set_heading($title); $PAGE->set_heading($insightinfo->contextname);
echo $OUTPUT->header(); echo $OUTPUT->header();

View File

@ -0,0 +1,34 @@
<?php
// 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/>.
/**
* Links and settings
*
* Contains settings used by insights report.
*
* @package report_insights
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die;
// Just a link to course report.
$ADMIN->add('reports', new admin_externalpage('reportinsights', get_string('insights', 'report_insights'),
$CFG->wwwroot . "/report/insights/insights.php?contextid=" . SYSCONTEXTID, 'moodle/analytics:listinsights'));
// No report settings.
$settings = null;

View File

@ -30,34 +30,33 @@
Example context (json): Example context (json):
{ {
"samplelink": "<a href=\"#\">Link</a>", "sampleimage": "<a href=\"#\">Link</a>",
"sampledescription": "Sample description", "sampledescription": "Sample description",
"predictionstyle": "alert alert-success", "style": "success",
"outcomeicon": {
"attributes": [
{"name": "src", "value": "https://moodle.org/logo/moodle-logo.svg" }
]
},
"predictiondisplayvalue": "This dev will understand it" "predictiondisplayvalue": "This dev will understand it"
} }
}} }}
<div class="card"> <tr>
<div class="row m-a-1"> <td class="col-sm-6">
{{#samplelink}} {{#sampleimage}}
<div class="col-sm-1 span1 m-b-1"> {{{sampleimage}}}
{{{samplelink}}} {{/sampleimage}}
</div> {{{sampledescription}}}
<div class="col-sm-3 span3 m-b-1"> </td>
{{{sampledescription}}} <td class="{{#style}}table-{{style}}{{/style}} col-sm-4">
</div> {{#outcomeicon}}
{{/samplelink}} {{> core/pix_icon}}
{{^samplelink}} {{/outcomeicon}}
<div class="col-sm-4 span4 m-b-1"> <span>{{predictiondisplayvalue}}</span>
{{{sampledescription}}} </td>
</div> <td class="col-sm-2">
{{/samplelink}} {{#actions}}
<div class="col-sm-4 span4"> {{> core/action_menu}}
<div class="{{predictionstyle}}">{{predictiondisplayvalue}}</div> {{/actions}}
</div> </td>
<div class="col-sm-4 span4"> </tr>
{{#actions}}
{{> core/action_menu}}
{{/actions}}
</div>
</div>
</div>

View File

@ -30,17 +30,40 @@
Example context (json): Example context (json):
{ {
"samplelink": "<a href=\"#\">Link</a>", "insightname": "Best insight ever",
"sampleimage": "<a href=\"#\">Link</a>",
"sampledescription": "Sample description", "sampledescription": "Sample description",
"predictionstyle": "alert alert-success", "style": "success",
"predictiondisplayvalue": "This dev will success", "outcomeicon": {
"attributes": [
{"name": "src", "value": "https://moodle.org/logo/moodle-logo.svg" }
]
},
"predictiondisplayvalue": "This dev will understand it",
"calculations": [ "calculations": [
{ {
"style": "alert alert-success", "style": "success",
"outcomeicon": {
"attributes": [
{"name": "src", "value": "https://moodle.org/logo/moodle-logo.svg" }
]
},
"name": "Indicator 1", "name": "Indicator 1",
"displayvalue": "yes" "displayvalue": "yes"
}, { }, {
"style": "alert alert-warning", "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", "name": "Indicator 2",
"displayvalue": "20%" "displayvalue": "20%"
} }
@ -48,12 +71,38 @@
} }
}} }}
<h2>{{#str}}prediction, report_insights{{/str}}</h2> <h2 class="m-b-2">{{#str}}insightprediction, report_insights, {{insightname}} {{/str}}</h2>
{{> report_insights/insight}} <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>
<h3>{{#str}} predictiondetails, report_insights {{/str}}</h3> <table class="generaltable prediction-calculations">
<div class="container prediction-calculations m-t-2"> <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}} {{#calculations}}
<div class="{{style}}">{{name}} - {{displayvalue}}</div> <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>
{{/calculations}} {{/calculations}}
</div> </tbody>
</table>
{{#nocalculations}}
{{> core/notification_info}}
{{/nocalculations}}

View File

@ -30,16 +30,27 @@
Example context (json): Example context (json):
{ {
"insightname": "Best insight ever",
"insights": [ "insights": [
{ {
"samplelink": "<a href=\"#\">Link</a>", "sampleimage": "<a href=\"#\">Link</a>",
"sampledescription": "Sample description", "sampledescription": "Sample description",
"predictionstyle": "alert alert-success", "style": "success",
"outcomeicon": {
"attributes": [
{"name": "src", "value": "https://moodle.org/logo/moodle-logo.svg" }
]
},
"predictiondisplayvalue": "This dev will understand it" "predictiondisplayvalue": "This dev will understand it"
}, { }, {
"samplelink": "<a href=\"#\">Any renderable</a>", "sampleimage": "<a href=\"#\">Any renderable</a>",
"sampledescription": "Another sample description", "sampledescription": "Another sample description",
"predictionstyle": "alert alert-danger", "style": "danger",
"outcomeicon": {
"attributes": [
{"name": "src", "value": "https://moodle.org/logo/moodle-logo.svg" }
]
},
"predictiondisplayvalue": "This dev will not understand it" "predictiondisplayvalue": "This dev will not understand it"
} }
], ],
@ -53,14 +64,24 @@
</div> </div>
{{/modelselector}} {{/modelselector}}
<h3>{{#str}} insights, report_insights {{/str}}</h3> <h2 class="m-b-2">{{{insightname}}}</h2>
{{^noinsights}} {{^noinsights}}
{{{ pagingbar }}} {{{ pagingbar }}}
<div class="insights-list"> <table class="generaltable insights-list">
{{#insights}} <caption>{{#str}}insights, report_insights{{/str}}</caption>
{{> report_insights/insight}} <thead>
{{/insights}} <tr>
</div> <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 }}} {{{ pagingbar }}}
{{/noinsights}} {{/noinsights}}
{{#noinsights}} {{#noinsights}}

View File

@ -24,6 +24,6 @@
defined('MOODLE_INTERNAL') || die(); defined('MOODLE_INTERNAL') || die();
$plugin->version = 2017051500; // The current plugin version (Date: YYYYMMDDXX). $plugin->version = 2017051501; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2017050500; // Requires this Moodle version. $plugin->requires = 2017050500; // Requires this Moodle version.
$plugin->component = 'report_insights'; // Full name of the plugin (used for diagnostics). $plugin->component = 'report_insights'; // Full name of the plugin (used for diagnostics).

View File

@ -0,0 +1,62 @@
{{!
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

@ -0,0 +1,108 @@
{{!
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

@ -0,0 +1,91 @@
{{!
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}}