MDL-59153 report_insights: UI improvements
authorDavid Monllao <davidm@moodle.com>
Thu, 31 Aug 2017 16:01:50 +0000 (18:01 +0200)
committerDavid Monllao <davidm@moodle.com>
Fri, 1 Sep 2017 09:20:26 +0000 (11:20 +0200)
- Accessible info tables
- Outcome icons
- Navigation bar for missing contexts
- Heading contents standarisation

15 files changed:
lang/en/analytics.php
report/insights/classes/output/insight.php
report/insights/classes/output/insights_list.php
report/insights/insights.php
report/insights/lang/en/report_insights.php
report/insights/lib.php
report/insights/prediction.php
report/insights/settings.php [new file with mode: 0644]
report/insights/templates/insight.mustache
report/insights/templates/insight_details.mustache
report/insights/templates/insights_list.mustache
report/insights/version.php
theme/bootstrapbase/templates/report_insights/insight.mustache [new file with mode: 0644]
theme/bootstrapbase/templates/report_insights/insight_details.mustache [new file with mode: 0644]
theme/bootstrapbase/templates/report_insights/insights_list.mustache [new file with mode: 0644]

index 3555da2..2af347e 100644 (file)
@@ -54,7 +54,6 @@ $string['errorunexistingmodel'] = 'Unexisting model {$a}';
 $string['errorunknownaction'] = 'Unknown action';
 $string['eventpredictionactionstarted'] = 'Prediction action started';
 $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['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';
index 134283e..51c966d 100644 (file)
@@ -73,20 +73,22 @@ class insight implements \renderable, \templatable {
     public function export_for_template(\renderer_base $output) {
 
         $data = new \stdClass();
+        $data->insightname = format_string($this->model->get_target()->get_name());
 
         // Sample info (determined by the analyser).
         list($data->sampledescription, $samplerenderable) = $this->model->prediction_sample_description($this->prediction);
 
         // Sampleimage is a renderable we should pass it to HTML.
         if ($samplerenderable) {
-            $data->samplelink = $output->render($samplerenderable);
+            $data->sampleimage = $output->render($samplerenderable);
         }
 
         // Prediction info.
         $predictedvalue = $this->prediction->get_prediction_data()->prediction;
         $predictionid = $this->prediction->get_prediction_data()->id;
         $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);
         if ($actions) {
@@ -122,39 +124,58 @@ 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);
-            $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;
         }
 
+        if (empty($data->calculations)) {
+            $data->nocalculations = (object)array(
+                'message' => get_string('nodetailsavailable', 'report_insights'),
+                'closebutton' => false
+            );
+        }
+
         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 float $value
+     * @param \renderer_base $output
      * @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);
         switch ($outcome) {
             case \core_analytics\calculable::OUTCOME_NEUTRAL:
                 $style = '';
+                $text = get_string('outcomeneutral', 'report_insights');
+                $icon = 't/check';
                 break;
             case \core_analytics\calculable::OUTCOME_VERY_POSITIVE:
-                $style = 'alert alert-success';
+                $style = 'success';
+                $text = get_string('outcomeverypositive', 'report_insights');
+                $icon = 't/approve';
                 break;
             case \core_analytics\calculable::OUTCOME_OK:
-                $style = 'alert alert-info';
+                $style = 'info';
+                $text = get_string('outcomeok', 'report_insights');
+                $icon = 't/check';
                 break;
             case \core_analytics\calculable::OUTCOME_NEGATIVE:
-                $style = 'alert alert-warning';
+                $style = 'warning';
+                $text = get_string('outcomenegative', 'report_insights');
+                $icon = 'i/warning';
                 break;
             case \core_analytics\calculable::OUTCOME_VERY_NEGATIVE:
-                $style = 'alert alert-danger';
+                $style = 'danger';
+                $text = get_string('outcomeverynegative', 'report_insights');
+                $icon = 'i/warning';
                 break;
             default:
                 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_VERY_NEGATIVE or \core_analytics\calculable::OUTCOME_NEUTRAL');
         }
-        return $style;
+        $icon = new \pix_icon($icon, $text);
+        return array($style, $icon->export_for_template($output));
     }
 }
index 3f67c4a..6596750 100644 (file)
@@ -88,6 +88,8 @@ class insights_list implements \renderable, \templatable {
         global $PAGE;
 
         $data = new \stdClass();
+        $data->insightname = format_string($this->model->get_target()->get_name());
+
         $total = 0;
 
         if ($this->model->uses_insights()) {
index 4a1b53f..5e94ff5 100644 (file)
@@ -23,6 +23,7 @@
  */
 
 require_once(__DIR__ . '/../../config.php');
+require_once($CFG->libdir . '/adminlib.php');
 
 $contextid = required_param('contextid', PARAM_INT);
 $modelid = optional_param('modelid', false, PARAM_INT);
@@ -52,8 +53,13 @@ if ($modelid) {
     unset($othermodels[$modelid]);
 }
 
+// The URL in navigation only contains the 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) {
     $url->param('modelid', $modelid);
 }
@@ -61,6 +67,18 @@ if ($modelid) {
 $PAGE->set_url($url);
 $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');
 
 // No models with insights available at this context level.
@@ -74,7 +92,6 @@ $model = new \core_analytics\model($modelid);
 $insightinfo = new stdClass();
 $insightinfo->contextname = $context->get_context_name();
 $insightinfo->insightname = $model->get_target()->get_name();
-$title = get_string('insightinfo', 'analytics', $insightinfo);
 
 if (!$model->is_enabled()) {
     echo $renderer->render_model_disabled($insightinfo);
@@ -86,8 +103,8 @@ if (!$model->uses_insights()) {
     exit(0);
 }
 
-$PAGE->set_title($title);
-$PAGE->set_heading($title);
+$PAGE->set_title($insightinfo->insightname);
+$PAGE->set_heading($insightinfo->contextname);
 
 echo $OUTPUT->header();
 
index 17af8e9..a69600b 100644 (file)
  * @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['indicator'] = 'Indicator';
+$string['insightprediction'] = '{$a} prediction';
+$string['insight'] = 'Insight';
 $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['prediction'] = 'Prediction';
+$string['predictioncalculations'] = 'Indicator calculations';
 $string['predictiondetails'] = 'Prediction details';
+$string['nodetailsavailable'] = 'No prediction details are relevant.';
 $string['selectotherinsights'] = 'Select other insights...';
index 6ebc7a1..e63bc79 100644 (file)
@@ -36,20 +36,85 @@ function report_insights_extend_navigation_course($navigation, $course, $context
 
     if (has_capability('moodle/analytics:listinsights', $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);
+        $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::TYPE_SETTING,
+                null, null, new pix_icon('i/report', get_string('insights', 'report_insights')));
+            $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));
-            $settingsnode = navigation_node::create(get_string('insights', 'report_insights'), $url, navigation_node::TYPE_SETTING,
-                null, null, new pix_icon('i/settings', ''));
-            $navigation->add_node($settingsnode);
+            $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;
+}
index 8ef7ed0..bb67a02 100644 (file)
@@ -23,6 +23,7 @@
  */
 
 require_once(__DIR__ . '/../../config.php');
+require_once($CFG->libdir . '/adminlib.php');
 
 $predictionid = required_param('id', PARAM_INT);
 
@@ -37,12 +38,27 @@ $url = new \moodle_url('/report/insights/prediction.php', $params);
 $PAGE->set_url($url);
 $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');
 
 $insightinfo = new stdClass();
 $insightinfo->contextname = $context->get_context_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);
 if (!$modelready) {
@@ -55,8 +71,8 @@ if (!$model->uses_insights()) {
     exit(0);
 }
 
-$PAGE->set_title($title);
-$PAGE->set_heading($title);
+$PAGE->set_title($insightinfo->insightname);
+$PAGE->set_heading($insightinfo->contextname);
 
 echo $OUTPUT->header();
 
diff --git a/report/insights/settings.php b/report/insights/settings.php
new file mode 100644 (file)
index 0000000..49a4559
--- /dev/null
@@ -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;
index c7cad23..ad24102 100644 (file)
 
     Example context (json):
     {
-        "samplelink": "<a href=\"#\">Link</a>",
+        "sampleimage": "<a href=\"#\">Link</a>",
         "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"
     }
 }}
-<div class="card">
-    <div class="row m-a-1">
-        {{#samplelink}}
-            <div class="col-sm-1 span1 m-b-1">
-                {{{samplelink}}}
-            </div>
-            <div class="col-sm-3 span3 m-b-1">
-                {{{sampledescription}}}
-            </div>
-        {{/samplelink}}
-        {{^samplelink}}
-            <div class="col-sm-4 span4 m-b-1">
-                {{{sampledescription}}}
-            </div>
-        {{/samplelink}}
-        <div class="col-sm-4 span4">
-            <div class="{{predictionstyle}}">{{predictiondisplayvalue}}</div>
-        </div>
-        <div class="col-sm-4 span4">
-            {{#actions}}
-                    {{> core/action_menu}}
-            {{/actions}}
-        </div>
-    </div>
-</div>
+<tr>
+    <td class="col-sm-6">
+        {{#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}}
+    {{/actions}}
+    </td>
+</tr>
index 0cc241c..72940c9 100644 (file)
 
     Example context (json):
     {
-        "samplelink": "<a href=\"#\">Link</a>",
+        "insightname": "Best insight ever",
+        "sampleimage": "<a href=\"#\">Link</a>",
         "sampledescription": "Sample description",
-        "predictionstyle": "alert alert-success",
-        "predictiondisplayvalue": "This dev will success",
+        "style": "success",
+        "outcomeicon": {
+            "attributes": [
+                {"name": "src", "value": "https://moodle.org/logo/moodle-logo.svg" }
+            ]
+        },
+        "predictiondisplayvalue": "This dev will understand it",
         "calculations": [
             {
-                "style": "alert alert-success",
+                "style": "success",
+                "outcomeicon": {
+                    "attributes": [
+                        {"name": "src", "value": "https://moodle.org/logo/moodle-logo.svg" }
+                    ]
+                },
                 "name": "Indicator 1",
                 "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",
                 "displayvalue": "20%"
             }
     }
 }}
 
-<h2>{{#str}}prediction, report_insights{{/str}}</h2>
-{{> report_insights/insight}}
+<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>
 
-<h3>{{#str}} predictiondetails, report_insights {{/str}}</h3>
-<div class="container prediction-calculations m-t-2">
+<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}}
-        <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}}
-</div>
+    </tbody>
+</table>
+{{#nocalculations}}
+    {{> core/notification_info}}
+{{/nocalculations}}
index 0150ef3..ecb2689 100644 (file)
 
     Example context (json):
     {
+        "insightname": "Best insight ever",
         "insights": [
             {
-                "samplelink": "<a href=\"#\">Link</a>",
+                "sampleimage": "<a href=\"#\">Link</a>",
                 "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"
             }, {
-                "samplelink": "<a href=\"#\">Any renderable</a>",
+                "sampleimage": "<a href=\"#\">Any renderable</a>",
                 "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"
             }
         ],
     </div>
 {{/modelselector}}
 
-<h3>{{#str}} insights, report_insights {{/str}}</h3>
+<h2 class="m-b-2">{{{insightname}}}</h2>
 {{^noinsights}}
 {{{ pagingbar }}}
-<div class="insights-list">
-    {{#insights}}
-        {{> report_insights/insight}}
-    {{/insights}}
-</div>
+<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}}
index d7ab0df..8529b85 100644 (file)
@@ -24,6 +24,6 @@
 
 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->component = 'report_insights'; // Full name of the plugin (used for diagnostics).
diff --git a/theme/bootstrapbase/templates/report_insights/insight.mustache b/theme/bootstrapbase/templates/report_insights/insight.mustache
new file mode 100644 (file)
index 0000000..3e152a6
--- /dev/null
@@ -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>
diff --git a/theme/bootstrapbase/templates/report_insights/insight_details.mustache b/theme/bootstrapbase/templates/report_insights/insight_details.mustache
new file mode 100644 (file)
index 0000000..65dda0f
--- /dev/null
@@ -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}}
diff --git a/theme/bootstrapbase/templates/report_insights/insights_list.mustache b/theme/bootstrapbase/templates/report_insights/insights_list.mustache
new file mode 100644 (file)
index 0000000..ecb2689
--- /dev/null
@@ -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}}