Skip to content

Commit

Permalink
MDL-57791 insights: Clarify insights-prediction boundaries
Browse files Browse the repository at this point in the history
  • Loading branch information
David Monllao committed Jul 24, 2017
1 parent 1a84613 commit f9e7447
Show file tree
Hide file tree
Showing 17 changed files with 177 additions and 69 deletions.
5 changes: 5 additions & 0 deletions admin/tool/models/classes/analytics/target/no_teaching.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ public function prediction_actions(\core_analytics\prediction $prediction) {
$sampledata = $prediction->get_sample_data();
$course = $sampledata['course'];

$url = new \moodle_url('/course/view.php', array('id' => $course->id));
$pix = new \pix_icon('i/course', get_string('enrolledusers', 'enrol'));
$actions['viewcourse'] = new \core_analytics\prediction_action('viewcourse', $prediction,
$url, $pix, get_string('view'));

if (has_capability('moodle/course:enrolreview', $sampledata['context'])) {
$url = new \moodle_url('/enrol/users.php', array('id' => $course->id));
$pix = new \pix_icon('i/enrolusers', get_string('enrolledusers', 'enrol'));
Expand Down
52 changes: 36 additions & 16 deletions admin/tool/models/classes/output/models_list.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,26 +56,46 @@ public function export_for_template(\renderer_base $output) {
$modeldata = $model->export();

// Model predictions list.
$predictioncontexts = $model->get_predictions_contexts();
if ($predictioncontexts) {

foreach ($predictioncontexts as $contextid => $unused) {
// We prepare this to be used as single_select template options.
$context = \context::instance_by_id($contextid);
if (empty($context)) {
// The context may have been deleted.
unset($predictioncontexts[$contextid]);
continue;
if ($model->uses_insights()) {
$predictioncontexts = $model->get_predictions_contexts();
if ($predictioncontexts) {

foreach ($predictioncontexts as $contextid => $unused) {
// We prepare this to be used as single_select template options.
$context = \context::instance_by_id($contextid);
if (empty($context)) {
// The context may have been deleted.
unset($predictioncontexts[$contextid]);
continue;
}

// Special name for system level predictions as showing "System is not visually nice".
if ($contextid == SYSCONTEXTID) {
$contextname = get_string('allpredictions', 'tool_models');
} else {
$contextname = shorten_text($context->get_context_name(true, true), 90);
}
$predictioncontexts[$contextid] = $contextname;
}
\core_collator::asort($predictioncontexts);

if (!empty($predictioncontexts)) {
$url = new \moodle_url('/report/insights/insights.php', array('modelid' => $model->get_id()));
$singleselect = new \single_select($url, 'contextid', $predictioncontexts);
$modeldata->insights = $singleselect->export_for_template($output);
}
$predictioncontexts[$contextid] = shorten_text($context->get_context_name(true, true), 90);
}
\core_collator::asort($predictioncontexts);

if (!empty($predictioncontexts)) {
$url = new \moodle_url('/report/insights/insights.php', array('modelid' => $model->get_id()));
$singleselect = new \single_select($url, 'contextid', $predictioncontexts);
$modeldata->predictions = $singleselect->export_for_template($output);
if (empty($modeldata->insights)) {
if ($model->any_prediction_obtained()) {
$modeldata->noinsights = get_string('noinsights', 'analytics');
} else {
$modeldata->noinsights = get_string('nopredictionsyet', 'analytics');
}
}

} else {
$modeldata->noinsights = get_string('noinsightsmodel', 'analytics');
}

// Actions.
Expand Down
3 changes: 2 additions & 1 deletion admin/tool/models/lang/en/tool_models.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

$string['accuracy'] = 'Accuracy';
$string['allindicators'] = 'All indicators';
$string['allpredictions'] = 'All predictions';
$string['analysingsitedata'] = 'Analysing the site';
$string['analyticmodels'] = 'Analytic models';
$string['bettercli'] = 'To evaluate models and to get predictions are heavy processes, it is better to run them through command line interface';
Expand Down Expand Up @@ -53,6 +54,7 @@
$string['goodmodel'] = 'This is a good model and it can be used to predict, enable it to start getting predictions.';
$string['indicators'] = 'Indicators';
$string['info'] = 'Info';
$string['insights'] = 'Insights';
$string['labelstudentdropoutyes'] = 'Student at risk of dropping out';
$string['labelstudentdropoutno'] = 'Not at risk';
$string['labelteachingyes'] = 'Users with teaching capabilities have access to the course';
Expand Down Expand Up @@ -88,6 +90,5 @@
$string['trainingresults'] = 'Training results';
$string['trainmodels'] = 'Train models';
$string['viewlog'] = 'Log';
$string['viewpredictions'] = 'View model predictions';
$string['weeksenddateautomaticallyset'] = 'End date automatically set based on start date and the number of sections';
$string['weeksenddatedefault'] = 'End date would be automatically calculated from the course start date';
13 changes: 7 additions & 6 deletions admin/tool/models/templates/models_list.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
<th scope="col">{{#str}}enabled, tool_models{{/str}}</th>
<th scope="col">{{#str}}indicators, tool_models{{/str}}</th>
<th scope="col">{{#str}}modeltimesplitting, tool_models{{/str}}</th>
<th scope="col">{{#str}}viewpredictions, tool_models{{/str}}</th>
<th scope="col">{{#str}}insights, tool_models{{/str}}</th>
<th scope="col">{{#str}}actions{{/str}}</th>
</tr>
</thead>
Expand All @@ -72,12 +72,13 @@
{{#timesplitting}}{{timesplitting}}{{/timesplitting}}{{^timesplitting}}{{#str}}notdefined, tool_models{{/str}}{{/timesplitting}}
</td>
<td>
{{#predictions}}
{{! models_list renderer is responsible of sending one or the other}}
{{#insights}}
{{> core/single_select }}
{{/predictions}}
{{^predictions}}
{{#str}}nopredictionsyet, analytics{{/str}}
{{/predictions}}
{{/insights}}
{{#noinsights}}
{{.}}
{{/noinsights}}
</td>
<td>
{{#actions}}
Expand Down
2 changes: 0 additions & 2 deletions analytics/classes/course.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@
*/
class course implements \core_analytics\analysable {

const MIN_STUDENT_LOGS_PERCENT = 90;

protected static $instances = array();

protected $studentroles = [];
Expand Down
11 changes: 11 additions & 0 deletions analytics/classes/local/target/base.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,17 @@ abstract public function is_valid_sample($sampleid, \core_analytics\analysable $
*/
abstract protected function calculate_sample($sampleid, \core_analytics\analysable $analysable, $starttime = false, $endtime = false);

/**
* Is this target generating insights?
*
* Defaults to true.
*
* @return bool
*/
public static function uses_insights() {
return true;
}

/**
* Based on facts (processed by machine learning backends) by default.
*
Expand Down
28 changes: 28 additions & 0 deletions analytics/classes/model.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ public function __construct($model) {

if (is_scalar($model)) {
$model = $DB->get_record('analytics_models', array('id' => $model));
if (!$model) {
throw new \moodle_exception('errorunexistingmodel', 'analytics', '', $model);
}
}
$this->model = $model;
}
Expand Down Expand Up @@ -821,6 +824,31 @@ public function get_predictions_contexts() {
return $DB->get_records_sql($sql, array($this->model->id));
}

/**
* Has this model generated predictions?
*
* We don't check analytics_predictions table because targets have the ability to
* ignore some predicted values, if that is the case predictions are not even stored
* in db.
*
* @return bool
*/
public function any_prediction_obtained() {
global $DB;
return $DB->record_exists('analytics_predict_ranges',
array('modelid' => $this->model->id, 'timesplitting' => $this->model->timesplitting));
}

/**
* Whether this model generates insights or not (defined by the model's target).
*
* @return bool
*/
public function uses_insights() {
$target = $this->get_target();
return $target::uses_insights();
}

/**
* Whether predictions exist for this context.
*
Expand Down
3 changes: 3 additions & 0 deletions lang/en/analytics.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
$string['errorprocessornotready'] = 'The selected predictions processor is not ready: {$a}';
$string['errorsamplenotavailable'] = 'The predicted sample is not available anymore';
$string['errorunexistingtimesplitting'] = 'The selected time splitting method is not available';
$string['errorunexistingmodel'] = 'Unexisting model {$a}';
$string['errorunknownaction'] = 'Unknown action';
$string['eventactionclicked'] = 'Prediction action clicked';
$string['indicator:accessesafterend'] = 'Accesses after the end date';
Expand All @@ -61,6 +62,8 @@
$string['nocourses'] = 'No courses to analyse';
$string['nocoursestart'] = 'No course start';
$string['nodata'] = 'No data available';
$string['noinsightsmodel'] = 'This model does not generate insights';
$string['noinsights'] = 'No insights reported';
$string['nonewdata'] = 'No new data available';
$string['nonewtimeranges'] = 'No new time ranges, nothing to predict';
$string['nopredictionsyet'] = 'No predictions available yet';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
* Prediction view page.
* Single insight view page.
*
* @package report_insights
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
Expand All @@ -27,13 +27,13 @@
defined('MOODLE_INTERNAL') || die();

/**
* Prediction view page.
* Single insight view page.
*
* @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
*/
class prediction implements \renderable, \templatable {
class insight implements \renderable, \templatable {

/**
* @var \core_analytics\model
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
* Predictions list page.
* Insights list page.
*
* @package report_insights
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
Expand All @@ -27,13 +27,13 @@
defined('MOODLE_INTERNAL') || die();

/**
* Shows report_insights predictions list.
* Shows report_insights insights list.
*
* @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
*/
class predictions_list implements \renderable, \templatable {
class insights_list implements \renderable, \templatable {

/**
* @var \core_analytics\model
Expand Down Expand Up @@ -67,17 +67,29 @@ public function export_for_template(\renderer_base $output) {

$data = new \stdClass();

$predictions = $this->model->get_predictions($this->context);
if ($this->model->uses_insights()) {
$predictions = $this->model->get_predictions($this->context);

$data->predictions = array();
foreach ($predictions as $prediction) {
$predictionrenderable = new \report_insights\output\prediction($prediction, $this->model);
$data->predictions[] = $predictionrenderable->export_for_template($output);
$data->insights = array();
foreach ($predictions as $prediction) {
$insightrenderable = new \report_insights\output\insight($prediction, $this->model);
$data->insights[] = $insightrenderable->export_for_template($output);
}

if (empty($data->insights)) {
if ($this->model->any_prediction_obtained()) {
$data->noinsights = get_string('noinsights', 'analytics');
} else {
$data->noinsights = get_string('nopredictionsyet', 'analytics');
}
}
} else {
$data->noinsights = get_string('noinsights', 'analytics');
}

if (empty($data->predictions)) {
$notification = new \core\output\notification(get_string('nopredictionsyet', 'analytics'));
$data->nopredictions = $notification->export_for_template($output);
if (!empty($data->noinsights)) {
$notification = new \core\output\notification($data->noinsights);
$data->noinsights = $notification->export_for_template($output);
}

if ($this->othermodels) {
Expand Down
38 changes: 29 additions & 9 deletions report/insights/classes/output/renderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,25 +40,25 @@
class renderer extends plugin_renderer_base {

/**
* Renders the list of predictions
* Renders the list of insights
*
* @param renderable $renderable
* @return string HTML
*/
protected function render_predictions_list(renderable $renderable) {
protected function render_insights_list(renderable $renderable) {
$data = $renderable->export_for_template($this);
return parent::render_from_template('report_insights/predictions_list', $data);
return parent::render_from_template('report_insights/insights_list', $data);
}

/**
* Renders a prediction
* Renders an insight
*
* @param renderable $renderable
* @return string HTML
*/
protected function render_prediction(renderable $renderable) {
protected function render_insight(renderable $renderable) {
$data = $renderable->export_for_template($this);
return parent::render_from_template('report_insights/prediction_details', $data);
return parent::render_from_template('report_insights/insight_details', $data);
}

/**
Expand All @@ -82,20 +82,40 @@ public function render_model_disabled($insightinfo) {
}

/**
* Model without predictions info.
* Model without insights info.
*
* @param \context $context
* @return string HTML
*/
public function render_no_predictions(\context $context) {
public function render_no_insights(\context $context) {
global $OUTPUT, $PAGE;

// We don't want to disclose the name of the model if it has not been enabled.
$PAGE->set_title($context->get_context_name());
$PAGE->set_heading($context->get_context_name());

$output = $OUTPUT->header();
$output .= $OUTPUT->notification(get_string('nopredictionsyet', 'analytics'), \core\output\notification::NOTIFY_INFO);
$output .= $OUTPUT->notification(get_string('noinsights', 'analytics'), \core\output\notification::NOTIFY_INFO);
$output .= $OUTPUT->footer();

return $output;
}

/**
* Model which target does not generate insights.
*
* @param \context $context
* @return string HTML
*/
public function render_no_insights_model(\context $context) {
global $OUTPUT, $PAGE;

// We don't want to disclose the name of the model if it has not been enabled.
$PAGE->set_title($context->get_context_name());
$PAGE->set_heading($context->get_context_name());

$output = $OUTPUT->header();
$output .= $OUTPUT->notification(get_string('noinsightsmodel', 'analytics'), \core\output\notification::NOTIFY_INFO);
$output .= $OUTPUT->footer();

return $output;
Expand Down
Loading

0 comments on commit f9e7447

Please sign in to comment.