Competency backend/frontend
This commit is contained in:
parent
4a97078405
commit
54a8823bbd
2
amd/build/page-edit-plan.min.js
vendored
2
amd/build/page-edit-plan.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
amd/build/studyplan-editor-components.min.js
vendored
2
amd/build/studyplan-editor-components.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -26,6 +26,7 @@ import Debugger from './util/debugger';
|
||||||
import {load_strings} from './util/string-helper';
|
import {load_strings} from './util/string-helper';
|
||||||
import {ProcessStudyplan} from './studyplan-processor';
|
import {ProcessStudyplan} from './studyplan-processor';
|
||||||
import {download,upload} from './downloader';
|
import {download,upload} from './downloader';
|
||||||
|
import {studyplanTiming} from './util/date-helper';
|
||||||
|
|
||||||
import PortalVue from './portal-vue/portal-vue.esm';
|
import PortalVue from './portal-vue/portal-vue.esm';
|
||||||
Vue.use(PortalVue);
|
Vue.use(PortalVue);
|
||||||
|
@ -128,8 +129,8 @@ export function init(contextid,categoryid,options) {
|
||||||
}])[0].done(function(response){
|
}])[0].done(function(response){
|
||||||
const timingval = { future: 0, present: 1, past: 2, };
|
const timingval = { future: 0, present: 1, past: 2, };
|
||||||
response.sort((a,b) => {
|
response.sort((a,b) => {
|
||||||
const timinga = TSComponents.studyplanTiming(a);
|
const timinga = studyplanTiming(a);
|
||||||
const timingb = TSComponents.studyplanTiming(b);
|
const timingb = studyplanTiming(b);
|
||||||
|
|
||||||
let t = timingval[timinga] - timingval[timingb];
|
let t = timingval[timinga] - timingval[timingb];
|
||||||
if(t == 0){
|
if(t == 0){
|
||||||
|
|
|
@ -234,6 +234,10 @@ export default {
|
||||||
completion_not_configured: "completion_not_configured",
|
completion_not_configured: "completion_not_configured",
|
||||||
configure_completion: "configure_completion",
|
configure_completion: "configure_completion",
|
||||||
},
|
},
|
||||||
|
competency: {
|
||||||
|
competency_not_configured: "competency_not_configured",
|
||||||
|
configure_competency: "configure_competency",
|
||||||
|
},
|
||||||
badge: {
|
badge: {
|
||||||
share_badge: "share_badge",
|
share_badge: "share_badge",
|
||||||
dateissued: "dateissued",
|
dateissued: "dateissued",
|
||||||
|
@ -2992,6 +2996,7 @@ export default {
|
||||||
`,
|
`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//TAG: Course item
|
||||||
Vue.component('t-item-course', {
|
Vue.component('t-item-course', {
|
||||||
props: {
|
props: {
|
||||||
value:{
|
value:{
|
||||||
|
@ -3032,7 +3037,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
configurationState(){
|
configurationState(){
|
||||||
if(this.hasGrades() || this.hasCompletions()) {
|
if(this.hasGrades() || this.hasCompletions() || this.hasCompetencies()) {
|
||||||
return "t-configured-ok";
|
return "t-configured-ok";
|
||||||
} else {
|
} else {
|
||||||
return "t-configured-alert";
|
return "t-configured-alert";
|
||||||
|
@ -3040,7 +3045,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
configurationIcon(){
|
configurationIcon(){
|
||||||
if(this.hasGrades() || this.hasCompletions()) {
|
if(this.hasGrades() || this.hasCompletions() || this.hasCompetencies()) {
|
||||||
return "check";
|
return "check";
|
||||||
} else {
|
} else {
|
||||||
return "exclamation-circle";
|
return "exclamation-circle";
|
||||||
|
@ -3080,6 +3085,12 @@ export default {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
hasCompetencies() {
|
||||||
|
if(this.value.course.competency && this.value.course.competency.competencies) {
|
||||||
|
return (this.value.course.competency.competencies.length > 0);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
includeChanged(newValue,g){
|
includeChanged(newValue,g){
|
||||||
call([{
|
call([{
|
||||||
methodname: 'local_treestudyplan_include_grade',
|
methodname: 'local_treestudyplan_include_grade',
|
||||||
|
@ -3177,6 +3188,11 @@ export default {
|
||||||
v-model='value.course.completion'
|
v-model='value.course.completion'
|
||||||
:course='value.course'
|
:course='value.course'
|
||||||
></t-item-course-completion>
|
></t-item-course-completion>
|
||||||
|
<t-item-course-competency
|
||||||
|
v-if='!!value.course.competency'
|
||||||
|
v-model='value.course.competency'
|
||||||
|
:course='value.course'
|
||||||
|
></t-item-course-competency>
|
||||||
|
|
||||||
<template #modal-footer="{ ok, cancel, hide }" >
|
<template #modal-footer="{ ok, cancel, hide }" >
|
||||||
<a href='#' @click='$emit("deleterq")' class="text-danger"
|
<a href='#' @click='$emit("deleterq")' class="text-danger"
|
||||||
|
@ -3383,7 +3399,123 @@ export default {
|
||||||
`,
|
`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//TAG: Course competency
|
||||||
|
Vue.component('t-item-course-competency',{
|
||||||
|
props: {
|
||||||
|
value : {
|
||||||
|
type: Object,
|
||||||
|
default: function(){ return {};},
|
||||||
|
},
|
||||||
|
guestmode: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
course: {
|
||||||
|
type: Object,
|
||||||
|
default: function(){ return {};},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
text: strings.competency,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
created(){
|
||||||
|
const self = this;
|
||||||
|
// Get text strings for condition settings
|
||||||
|
let stringkeys = [];
|
||||||
|
for(const key in this.text){
|
||||||
|
stringkeys.push({ key: key, component: 'local_treestudyplan'});
|
||||||
|
}
|
||||||
|
get_strings(stringkeys).then(function(strings){
|
||||||
|
let i = 0;
|
||||||
|
for(const key in self.text){
|
||||||
|
self.text[key] = strings[i];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
hasCompletions() {
|
||||||
|
if(this.value.conditions) {
|
||||||
|
for(const cgroup of this.value.conditions){
|
||||||
|
if(cgroup.items && cgroup.items.length > 0){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
completion_icon(completion) {
|
||||||
|
switch(completion){
|
||||||
|
case "progress":
|
||||||
|
return "exclamation-circle";
|
||||||
|
case "complete":
|
||||||
|
return "check-circle";
|
||||||
|
case "complete-pass":
|
||||||
|
return "check-circle";
|
||||||
|
case "complete-fail":
|
||||||
|
return "times-circle";
|
||||||
|
default: // case "incomplete"
|
||||||
|
return "circle-o";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
completion_tag(cgroup){
|
||||||
|
return cgroup.completion?'completed':'incomplete';
|
||||||
|
},
|
||||||
|
|
||||||
|
pathtags(competency,use_idnumber=false) {
|
||||||
|
const path = competency.path;
|
||||||
|
let s = "";
|
||||||
|
for (const ix in path) {
|
||||||
|
const p = path[ix];
|
||||||
|
if ( ix > 0) {
|
||||||
|
s += " / ";
|
||||||
|
}
|
||||||
|
s += `<a href="/admin/tool/lp/competencies.php?competencyid=${p.id}">${(use_idnumber)?p.idnumber:p.shortname}</a>`;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
template: `
|
||||||
|
<table class="r-item-course-grade-details">
|
||||||
|
<tr v-if="value.competencies.length == 0">
|
||||||
|
<td colspan='2'>{{text.competencies_not_configured}}!
|
||||||
|
<br><a :href="'/admin/tool/lp/coursecompetencies.php?courseid='+course.id" target='_blank'>{{text.configure_competencies}}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<template v-else>
|
||||||
|
<tr v-for='c in value.competencies'>
|
||||||
|
<td><a href='#' v-b-modal="'modal-competency-id-'+c.id"><span v-html='c.shortname'></span></a></td>
|
||||||
|
<td v-if="c.description">
|
||||||
|
<span v-html='c.description'></span>
|
||||||
|
</td>
|
||||||
|
<b-modal :id="'modal-competency-id-'+c.id"
|
||||||
|
:title="c.path.join(' / ')"
|
||||||
|
size="lg"
|
||||||
|
ok-only
|
||||||
|
centered
|
||||||
|
scrollable
|
||||||
|
>
|
||||||
|
<template #modal-header>
|
||||||
|
<div>
|
||||||
|
<h1><i class="fa fa-certificate"></i>
|
||||||
|
<a :href="'/admin/tool/lp/competencies.php?competencyid='+c.id'" target="_blank"
|
||||||
|
>{{c.shortname}}</a
|
||||||
|
></h1>
|
||||||
|
<div>{{ pathtags(c) }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
</b-modal>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
</table>
|
||||||
|
`,
|
||||||
|
});
|
||||||
/************************************
|
/************************************
|
||||||
* *
|
* *
|
||||||
* Toolbox list components *
|
* Toolbox list components *
|
||||||
|
|
|
@ -68,6 +68,7 @@ abstract class aggregator {
|
||||||
// And this is faster than any dynamic method.
|
// And this is faster than any dynamic method.
|
||||||
return [
|
return [
|
||||||
"core", // Use moodle core completion.
|
"core", // Use moodle core completion.
|
||||||
|
"competency",
|
||||||
"bistate",
|
"bistate",
|
||||||
"tristate", // Deprecated.
|
"tristate", // Deprecated.
|
||||||
];
|
];
|
||||||
|
@ -161,23 +162,27 @@ abstract class aggregator {
|
||||||
*/
|
*/
|
||||||
abstract public function grade_completion(gradeinfo $gradeinfo, $userid);
|
abstract public function grade_completion(gradeinfo $gradeinfo, $userid);
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if Aggregation method makes use of "required grades" in a course/module.
|
|
||||||
* @return bool True if Aggregation method makes use of "required grades" in a course/module.
|
|
||||||
*/
|
|
||||||
abstract public function use_required_grades();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if aggregation method makes use of required grades
|
|
||||||
* @return bool True if aggregation method makes use of
|
|
||||||
*/
|
|
||||||
abstract public function use_item_conditions();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if the aggregation method uses core_completion, or treestudyplan custom completion.
|
* Determine if the aggregation method uses core_completion, or treestudyplan custom completion.
|
||||||
* @return bool True if the aggregation method uses core_completion
|
* @return bool True if the aggregation method uses core_completion
|
||||||
*/
|
*/
|
||||||
public function usecorecompletioninfo() {
|
public function use_corecompletioninfo() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the aggregation method uses course competencies,
|
||||||
|
* @return bool True if the aggregation method uses course competencies
|
||||||
|
*/
|
||||||
|
public function use_coursecompetencies() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the aggregation method uses manual activity selection,
|
||||||
|
* @return bool True if the aggregation method uses manual activity selection
|
||||||
|
*/
|
||||||
|
public function use_manualactivityselection() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -196,8 +201,6 @@ abstract class aggregator {
|
||||||
*/
|
*/
|
||||||
public static function basic_structure($value = VALUE_REQUIRED) : \external_description {
|
public static function basic_structure($value = VALUE_REQUIRED) : \external_description {
|
||||||
return new \external_single_structure([
|
return new \external_single_structure([
|
||||||
"useRequiredGrades" => new \external_value(PARAM_BOOL, 'id of studyplan'),
|
|
||||||
"useItemConditions" => new \external_value(PARAM_BOOL, 'name of studyplan'),
|
|
||||||
], "Aggregator requirements", $value);
|
], "Aggregator requirements", $value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,8 +210,6 @@ abstract class aggregator {
|
||||||
*/
|
*/
|
||||||
public function basic_model() {
|
public function basic_model() {
|
||||||
return [
|
return [
|
||||||
"useRequiredGrades" => $this->use_required_grades(),
|
|
||||||
"useItemConditions" => $this->use_item_conditions(),
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
302
classes/coursecompetencyinfo.php
Normal file
302
classes/coursecompetencyinfo.php
Normal file
|
@ -0,0 +1,302 @@
|
||||||
|
<?php
|
||||||
|
// This file is part of the Studyplan plugin for Moodle
|
||||||
|
//
|
||||||
|
// 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 <https://www.gnu.org/licenses/>.
|
||||||
|
/**
|
||||||
|
* Class to collect course completion info for a given course
|
||||||
|
* @package local_treestudyplan
|
||||||
|
* @copyright 2023 P.M. Kuipers
|
||||||
|
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace local_treestudyplan;
|
||||||
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
|
require_once($CFG->libdir.'/externallib.php');
|
||||||
|
require_once($CFG->libdir.'/gradelib.php');
|
||||||
|
require_once($CFG->dirroot.'/course/lib.php');
|
||||||
|
|
||||||
|
use core_competency\course_competency;
|
||||||
|
use core_competency\competency;
|
||||||
|
use core_competency\api as c_api;
|
||||||
|
use stdClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class to collect course competencty info for a given course
|
||||||
|
*/
|
||||||
|
class coursecompetencyinfo {
|
||||||
|
/** @var \stdClass */
|
||||||
|
private $course;
|
||||||
|
/** @var \course_modinfo */
|
||||||
|
private $modinfo;
|
||||||
|
/** @var studyitem */
|
||||||
|
private $studyitem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Course id of relevant course
|
||||||
|
*/
|
||||||
|
public function id() {
|
||||||
|
return $this->course->id;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Construct new object for a given course
|
||||||
|
* @param \stdClass $course Course database record
|
||||||
|
*/
|
||||||
|
public function __construct($course, $studyitem) {
|
||||||
|
global $DB;
|
||||||
|
$this->course = $course;
|
||||||
|
$this->studyitem = $studyitem;
|
||||||
|
$this->completion = new \completion_info($this->course);
|
||||||
|
$this->modinfo = get_fast_modinfo($this->course);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic competency info structure for individual competency stats
|
||||||
|
* @param $recurse True if child competencies may be included
|
||||||
|
*/
|
||||||
|
public static function competencyinfo_structure($recurse=true) : \external_description {
|
||||||
|
$struct = [
|
||||||
|
"id" => new \external_value(PARAM_INT, 'competency id'),
|
||||||
|
"shortname" => new \external_value(PARAM_RAW, 'competency short name'),
|
||||||
|
"idnumber" => new \external_value(PARAM_TEXT, 'competency ID number'),
|
||||||
|
"description" => new \external_value(PARAM_RAW, 'competency description'),
|
||||||
|
"path" => new \external_multiple_structure(new \external_single_structure([
|
||||||
|
"id" => new \external_value(PARAM_INT),
|
||||||
|
"shortname" => new \external_value(PARAM_RAW),
|
||||||
|
"idnumber" => new \external_value(PARAM_TEXT),
|
||||||
|
]), 'competency path'),
|
||||||
|
"grade" => new \external_value(PARAM_TEXT, 'competency grade', VALUE_OPTIONAL),
|
||||||
|
"coursegrade" => new \external_value(PARAM_TEXT, 'course competency grade', VALUE_OPTIONAL),
|
||||||
|
"proficient" => new \external_value(PARAM_BOOL, 'competency proficiency',VALUE_OPTIONAL),
|
||||||
|
"courseproficient" => new \external_value(PARAM_BOOL, 'course competency proficiency',VALUE_OPTIONAL),
|
||||||
|
"nproficient" => new \external_value(PARAM_INT, 'number of students with proficiency',VALUE_OPTIONAL),
|
||||||
|
"ncourseproficient" => new \external_value(PARAM_INT, 'number of students with course proficiency',VALUE_OPTIONAL),
|
||||||
|
"count" => new \external_value(PARAM_INT, 'number of students in stats',VALUE_OPTIONAL),
|
||||||
|
];
|
||||||
|
if($recurse) {
|
||||||
|
$struct["children"] = new \external_multiple_structure(self::competencyinfo_structure(false),'child competencies',VALUE_OPTIONAL);
|
||||||
|
}
|
||||||
|
return new \external_single_structure($struct, 'course completion info');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Webservice structure for editor info
|
||||||
|
* @param int $value Webservice requirement constant
|
||||||
|
*/
|
||||||
|
public static function editor_structure($value = VALUE_REQUIRED) : \external_description {
|
||||||
|
return new \external_single_structure([
|
||||||
|
"competencies" => new \external_multiple_structure(self::competencyinfo_structure(), 'competencies'),
|
||||||
|
"fproficient" => new \external_value(PARAM_FLOAT, 'fraction of completion for total course proficiency ',VALUE_OPTIONAL),
|
||||||
|
"fcourseproficient" => new \external_value(PARAM_FLOAT, 'fraction of completion for total course proficienct',VALUE_OPTIONAL),
|
||||||
|
], 'course completion info', $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Webservice structure for userinfo
|
||||||
|
* @param int $value Webservice requirement constant
|
||||||
|
*/
|
||||||
|
public static function user_structure($value = VALUE_REQUIRED) : \external_description {
|
||||||
|
return new \external_single_structure([
|
||||||
|
"progress" => new \external_value(PARAM_INT, 'number completed competencies'),
|
||||||
|
"competencies" => new \external_multiple_structure(self::competencyinfo_structure(), 'competencies'),
|
||||||
|
"count" => new \external_value(PARAM_INT, 'number of competencies',VALUE_OPTIONAL),
|
||||||
|
], 'course completion info', $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create basic competency information model from competency
|
||||||
|
* @param Object $competency
|
||||||
|
*/
|
||||||
|
private function competencyinfo_model($competency) : array {
|
||||||
|
|
||||||
|
$path = [];
|
||||||
|
foreach ($competency->get_ancestors() as $c) {
|
||||||
|
$competencypath[] = $c->get('shortname');
|
||||||
|
$path[] = [
|
||||||
|
'id' => $c->get('id'),
|
||||||
|
'shortname' => $c->get('shortname'),
|
||||||
|
'idnumber' => $c->get('idnumber'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$path[] = [
|
||||||
|
'id' => $competency->get('id'),
|
||||||
|
'shortname' => $competency->get('shortname'),
|
||||||
|
'idnumber' => $competency->get('idnumber'),
|
||||||
|
];
|
||||||
|
|
||||||
|
$model = [
|
||||||
|
'id' => $competency->get('id'),
|
||||||
|
'shortname' => $competency->get('shortname'),
|
||||||
|
'idnumber' => $competency->get('idnumber'),
|
||||||
|
'description' => $competency->get('description'),
|
||||||
|
'path' => $path,
|
||||||
|
];
|
||||||
|
|
||||||
|
return $model;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Webservice model for editor info
|
||||||
|
* @param int[] $studentlist List of user id's to use for checking issueing progress within a study plan
|
||||||
|
* @return array Webservice data model
|
||||||
|
*/
|
||||||
|
public function editor_model(array $studentlist = null) {
|
||||||
|
$coursecompetencies = $this->course_competencies();
|
||||||
|
// Next create the data model, and check user proficiency for each competency.
|
||||||
|
|
||||||
|
$count = 0;
|
||||||
|
$nproficient = 0;
|
||||||
|
$ncourseproficient = 0;
|
||||||
|
|
||||||
|
foreach($coursecompetencies as $c) {
|
||||||
|
$stats = $this->proficiency_stats($c,$studentlist);
|
||||||
|
$count += $stats->count;
|
||||||
|
$nproficient += $stats->nproficient;
|
||||||
|
$ncourseproficient += $stats->ncourseproficient;
|
||||||
|
|
||||||
|
$ci = $this->competencyinfo_model($c);
|
||||||
|
// Copy proficiency stats to model.
|
||||||
|
foreach ((array)$stats as $key => $value) {
|
||||||
|
$ci[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get one level of children
|
||||||
|
$dids = competency::get_descendants_ids($c);
|
||||||
|
if(count($dids) > 0) {
|
||||||
|
$children = [];
|
||||||
|
foreach($dids as $did) {
|
||||||
|
$cc = new competency($did);
|
||||||
|
$cci = $this->competencyinfo_model($cc);
|
||||||
|
$children[] = $cci;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ci["children"] = $children;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cis[] = $ci;
|
||||||
|
}
|
||||||
|
$info = [
|
||||||
|
"competencies" => $cis,
|
||||||
|
"fproficient" => (float)($nproficient)/(float)($count),
|
||||||
|
"fcourseproficient" => (float)($ncourseproficient)/(float)($count),
|
||||||
|
];
|
||||||
|
return $info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Webservice model for user course completion info
|
||||||
|
* @param int $userid ID of user to check specific info for
|
||||||
|
* @return array Webservice data model
|
||||||
|
*/
|
||||||
|
public function user_model($userid) {
|
||||||
|
$competencies = $this->course_competencies();
|
||||||
|
$progress = 0;
|
||||||
|
|
||||||
|
$cis = [];
|
||||||
|
foreach ($competencies as $c) {
|
||||||
|
$ci = $this->competencyinfo_model($c,$userid);
|
||||||
|
// Add user info if $userid is set.
|
||||||
|
$p = $this->proficiency($c,$userid);
|
||||||
|
// Copy proficiency info to model.
|
||||||
|
foreach ((array)$p as $key => $value) {
|
||||||
|
$ci[$key] = $value;
|
||||||
|
}
|
||||||
|
if ($p->proficient || $p->courseproficient) {
|
||||||
|
$progress += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get one level of children
|
||||||
|
$dids = competency::get_descendants_ids($c);
|
||||||
|
if(count($dids) > 0) {
|
||||||
|
$children = [];
|
||||||
|
foreach($dids as $did) {
|
||||||
|
$cc = new competency($did);
|
||||||
|
$cci = $this->competencyinfo_model($cc);
|
||||||
|
$cp = $this->proficiency($cc,$userid);
|
||||||
|
// Copy proficiency info to model.
|
||||||
|
foreach ((array)$cp as $key => $value) {
|
||||||
|
$cci[$key] = $value;
|
||||||
|
}
|
||||||
|
$children[] = $cci;
|
||||||
|
}
|
||||||
|
|
||||||
|
$ci["children"] = $children;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cis[] = $ci;
|
||||||
|
}
|
||||||
|
|
||||||
|
$info = [
|
||||||
|
'progress' => $progress,
|
||||||
|
"count" => count($competencies),
|
||||||
|
"competencies" => $cis,
|
||||||
|
];
|
||||||
|
|
||||||
|
return $info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the course's competencies with user status
|
||||||
|
* @return array of Competencies Webservice data model
|
||||||
|
*/
|
||||||
|
public function course_competencies() {
|
||||||
|
$list = [];
|
||||||
|
// First retrieve all the competencies associates with this course.
|
||||||
|
$coursecompetencies = c_api::list_course_competencies($this->course->id);
|
||||||
|
// Next create the data model, and check user proficiency for each competency.
|
||||||
|
foreach($coursecompetencies as $ccinfo) {
|
||||||
|
$list[] = $ccinfo['competency'];
|
||||||
|
}
|
||||||
|
return $list;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function proficiency_stats($competency,$studentlist) {
|
||||||
|
$r = new \stdClass();
|
||||||
|
$r->count = 0;
|
||||||
|
$r->nproficient = 0;
|
||||||
|
$r->ncourseproficient = 0;
|
||||||
|
|
||||||
|
foreach ($studentlist as $sid) {
|
||||||
|
$p = $this->proficiency($competency,$sid);
|
||||||
|
$r->count += 1;
|
||||||
|
$r->nproficient += ($p->proficient)?1:0;
|
||||||
|
$r->ncourseproficient += ($p->courseproficient)?1:0;
|
||||||
|
}
|
||||||
|
return $r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve course proficiency and overall proficiency for a competency and user
|
||||||
|
*
|
||||||
|
* @param \core_competency\competency $competency
|
||||||
|
* @param int $userid
|
||||||
|
*
|
||||||
|
* @return stdClass
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function proficiency($competency, $userid) {
|
||||||
|
$scale = $competency->get_scale();
|
||||||
|
$competencyid = $competency->get('id');
|
||||||
|
$uc = c_api::get_user_competency($userid, $competencyid);
|
||||||
|
$ucc = c_api::get_user_competency_in_course($this->course->id,$userid,$competencyid);
|
||||||
|
$r = new \stdClass();
|
||||||
|
$r->proficient = $uc->get('proficiency');
|
||||||
|
$r->courseproficient = $ucc->get('proficiency');
|
||||||
|
$r->grade = $scale->get_nearest_item($uc->get('grade'));
|
||||||
|
$r->coursegrade = $scale->get_nearest_item($ucc->get('grade'));
|
||||||
|
return $r;
|
||||||
|
}
|
||||||
|
}
|
|
@ -283,6 +283,7 @@ class courseinfo {
|
||||||
"grades" => new \external_multiple_structure(gradeinfo::editor_structure(),
|
"grades" => new \external_multiple_structure(gradeinfo::editor_structure(),
|
||||||
'grade list (legacy list)', VALUE_OPTIONAL),
|
'grade list (legacy list)', VALUE_OPTIONAL),
|
||||||
"completion" => corecompletioninfo::editor_structure(VALUE_OPTIONAL),
|
"completion" => corecompletioninfo::editor_structure(VALUE_OPTIONAL),
|
||||||
|
"competency" => coursecompetencyinfo::editor_structure(VALUE_OPTIONAL),
|
||||||
"timing" => new \external_value(PARAM_TEXT, '(past|present|future)'),
|
"timing" => new \external_value(PARAM_TEXT, '(past|present|future)'),
|
||||||
"startdate" => new \external_value(PARAM_TEXT, 'Course start date'),
|
"startdate" => new \external_value(PARAM_TEXT, 'Course start date'),
|
||||||
"enddate" => new \external_value(PARAM_TEXT, 'Course end date'),
|
"enddate" => new \external_value(PARAM_TEXT, 'Course end date'),
|
||||||
|
@ -297,10 +298,9 @@ class courseinfo {
|
||||||
/**
|
/**
|
||||||
* Webservice model for editor info
|
* Webservice model for editor info
|
||||||
* @param studyitem $studyitem Specify a specific study item to check gradable selections for. Leave empty to use default
|
* @param studyitem $studyitem Specify a specific study item to check gradable selections for. Leave empty to use default
|
||||||
* @param bool $usecorecompletioninfo Whether to use corecompletion info instead of custom selected gradables
|
|
||||||
* @return array Webservice data model
|
* @return array Webservice data model
|
||||||
*/
|
*/
|
||||||
public function editor_model($usecorecompletioninfo = false) {
|
public function editor_model() {
|
||||||
$contextinfo = new contextinfo($this->context);
|
$contextinfo = new contextinfo($this->context);
|
||||||
$timing = $this->timing();
|
$timing = $this->timing();
|
||||||
|
|
||||||
|
@ -329,16 +329,24 @@ class courseinfo {
|
||||||
];
|
];
|
||||||
|
|
||||||
if (isset($this->studyitem)) {
|
if (isset($this->studyitem)) {
|
||||||
if (!$usecorecompletioninfo) {
|
$aggregator = $this->studyitem->studyline()->studyplan()->aggregator();
|
||||||
|
|
||||||
|
if ($aggregator->use_manualactivityselection()) {
|
||||||
$gradables = gradeinfo::list_course_gradables($this->course, $this->studyitem );
|
$gradables = gradeinfo::list_course_gradables($this->course, $this->studyitem );
|
||||||
foreach ($gradables as $gradable) {
|
foreach ($gradables as $gradable) {
|
||||||
$info['grades'][] = $gradable->editor_model($this->studyitem);
|
$info['grades'][] = $gradable->editor_model($this->studyitem);
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
if ($aggregator->use_corecompletioninfo()) {
|
||||||
$cc = new corecompletioninfo($this->course, $this->studyitem);
|
$cc = new corecompletioninfo($this->course, $this->studyitem);
|
||||||
$studentlist = $this->studyitem->studyline()->studyplan()->find_linked_userids();
|
$studentlist = $this->studyitem->studyline()->studyplan()->find_linked_userids();
|
||||||
$info['completion'] = $cc->editor_model($studentlist);
|
$info['completion'] = $cc->editor_model($studentlist);
|
||||||
}
|
}
|
||||||
|
if ($aggregator->use_coursecompetencies()) {
|
||||||
|
$ci = new coursecompetencyinfo($this->course, $this->studyitem);
|
||||||
|
$studentlist = $this->studyitem->studyline()->studyplan()->find_linked_userids();
|
||||||
|
$info['competency'] = $ci->editor_model($studentlist);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return $info;
|
return $info;
|
||||||
}
|
}
|
||||||
|
@ -357,6 +365,7 @@ class courseinfo {
|
||||||
"ctxid" => new \external_value(PARAM_INT, 'course context id name'),
|
"ctxid" => new \external_value(PARAM_INT, 'course context id name'),
|
||||||
"grades" => new \external_multiple_structure(gradeinfo::user_structure(), 'grade list (legacy list)', VALUE_OPTIONAL),
|
"grades" => new \external_multiple_structure(gradeinfo::user_structure(), 'grade list (legacy list)', VALUE_OPTIONAL),
|
||||||
"completion" => corecompletioninfo::user_structure(VALUE_OPTIONAL),
|
"completion" => corecompletioninfo::user_structure(VALUE_OPTIONAL),
|
||||||
|
"competency" => coursecompetencyinfo::user_structure(VALUE_OPTIONAL),
|
||||||
"timing" => new \external_value(PARAM_TEXT, '(past|present|future)'),
|
"timing" => new \external_value(PARAM_TEXT, '(past|present|future)'),
|
||||||
"startdate" => new \external_value(PARAM_TEXT, 'Course start date'),
|
"startdate" => new \external_value(PARAM_TEXT, 'Course start date'),
|
||||||
"enddate" => new \external_value(PARAM_TEXT, 'Course end date'),
|
"enddate" => new \external_value(PARAM_TEXT, 'Course end date'),
|
||||||
|
@ -416,10 +425,9 @@ class courseinfo {
|
||||||
/**
|
/**
|
||||||
* Webservice model for user info
|
* Webservice model for user info
|
||||||
* @param int $userid ID of user to check specific info for
|
* @param int $userid ID of user to check specific info for
|
||||||
* @param bool $usecorecompletioninfo Whether to use corecompletion info instead of custom selected gradables
|
|
||||||
* @return array Webservice data model
|
* @return array Webservice data model
|
||||||
*/
|
*/
|
||||||
public function user_model($userid, $usecorecompletioninfo = false) {
|
public function user_model($userid) {
|
||||||
global $DB;
|
global $DB;
|
||||||
$contextinfo = new contextinfo($this->context);
|
$contextinfo = new contextinfo($this->context);
|
||||||
|
|
||||||
|
@ -439,16 +447,24 @@ class courseinfo {
|
||||||
'enrolled' => $this->is_enrolled_student($userid),
|
'enrolled' => $this->is_enrolled_student($userid),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
if (isset($this->studyitem)) {
|
if (isset($this->studyitem)) {
|
||||||
if (!$usecorecompletioninfo) {
|
$aggregator = $this->studyitem->studyline()->studyplan()->aggregator();
|
||||||
|
|
||||||
|
if ($aggregator->use_manualactivityselection()) {
|
||||||
$gradables = gradeinfo::list_studyitem_gradables($this->studyitem);
|
$gradables = gradeinfo::list_studyitem_gradables($this->studyitem);
|
||||||
foreach ($gradables as $gi) {
|
foreach ($gradables as $gi) {
|
||||||
$info['grades'][] = $gi->user_model($userid);
|
$info['grades'][] = $gi->user_model($userid);
|
||||||
}
|
}
|
||||||
} else if (isset($this->studyitem)) {
|
}
|
||||||
|
if ($aggregator->use_corecompletioninfo()) {
|
||||||
$cc = new corecompletioninfo($this->course, $this->studyitem);
|
$cc = new corecompletioninfo($this->course, $this->studyitem);
|
||||||
$info['completion'] = $cc->user_model($userid);
|
$info['completion'] = $cc->user_model($userid);
|
||||||
}
|
}
|
||||||
|
if ($aggregator->use_coursecompetencies()) {
|
||||||
|
$ci = new coursecompetencyinfo($this->course, $this->studyitem);
|
||||||
|
$info['competency'] = $ci->user_model($userid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return $info;
|
return $info;
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,21 +131,13 @@ class bistate_aggregator extends \local_treestudyplan\aggregator {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if Aggregation method makes use of "required grades" in a course/module.
|
* Determine if the aggregation method uses manual activity selection,
|
||||||
* @return bool True if Aggregation method makes use of "required grades" in a course/module.
|
* @return bool True if the aggregation method uses manual activity selection
|
||||||
*/
|
*/
|
||||||
public function use_required_grades() {
|
public function use_manualactivityselection() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if aggregation method makes use of required grades
|
|
||||||
* @return bool True if aggregation method makes use of
|
|
||||||
*/
|
|
||||||
public function use_item_conditions() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Aggregate completed/failed goals into one outcome
|
* Aggregate completed/failed goals into one outcome
|
||||||
* @param int[] $completions List of completions (completion class constants)
|
* @param int[] $completions List of completions (completion class constants)
|
||||||
|
|
283
classes/local/aggregators/competency_aggregator.php
Normal file
283
classes/local/aggregators/competency_aggregator.php
Normal file
|
@ -0,0 +1,283 @@
|
||||||
|
<?php
|
||||||
|
// This file is part of the Studyplan plugin for Moodle
|
||||||
|
//
|
||||||
|
// 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 <https://www.gnu.org/licenses/>.
|
||||||
|
/**
|
||||||
|
* Aggregate course results with moodle course completion
|
||||||
|
* @package local_treestudyplan
|
||||||
|
* @copyright 2023 P.M. Kuipers
|
||||||
|
* @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace local_treestudyplan\local\aggregators;
|
||||||
|
|
||||||
|
use core_competency\api as c_api;
|
||||||
|
|
||||||
|
use \local_treestudyplan\courseinfo;
|
||||||
|
use \local_treestudyplan\gradeinfo;
|
||||||
|
use \local_treestudyplan\studyitem;
|
||||||
|
use \local_treestudyplan\completion;
|
||||||
|
use local_treestudyplan\coursecompetencyinfo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aggregate course results with moodle course completion
|
||||||
|
*/
|
||||||
|
class competency_aggregator extends \local_treestudyplan\aggregator {
|
||||||
|
/** @var bool */
|
||||||
|
public const DEPRECATED = false;
|
||||||
|
/** @var stdClass */
|
||||||
|
private $agcfg = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve or initialize current config object
|
||||||
|
* @return stdClass
|
||||||
|
*/
|
||||||
|
private function cfg() {
|
||||||
|
if (empty($this->agcfg)) {
|
||||||
|
$this->agcfg = (object)[
|
||||||
|
'thresh_completed' => 0.66, // Minimum fraction that must be completed to aggregate as completed.
|
||||||
|
'use_failed' => true, // Support failed completion yes/no.
|
||||||
|
];
|
||||||
|
}
|
||||||
|
return $this->agcfg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create new instance of aggregation method
|
||||||
|
* @param string $configstr Aggregation configuration string
|
||||||
|
*/
|
||||||
|
public function __construct($configstr) {
|
||||||
|
// Allow public constructor for testing purposes.
|
||||||
|
$this->initialize($configstr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialize the aggregation method
|
||||||
|
* @param string $configstr Aggregation configuration string
|
||||||
|
*/
|
||||||
|
protected function initialize($configstr) {
|
||||||
|
// First initialize with the defaults.
|
||||||
|
foreach (["thresh_completed", ] as $key) {
|
||||||
|
$val = intval(get_config('local_treestudyplan', "competency_{$key}"));
|
||||||
|
if ($val >= 0 && $val <= 100) {
|
||||||
|
$this->cfg()->$key = floatval($val) / 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (["use_failed", ] as $key) {
|
||||||
|
$this->cfg()->$key = boolval(get_config('local_treestudyplan', "competency_{$key}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next, decode json.
|
||||||
|
$config = \json_decode($configstr, true);
|
||||||
|
|
||||||
|
if (is_array($config)) {
|
||||||
|
// Copy all valid config settings to this item.
|
||||||
|
foreach (["thresh_completed", ] as $key) {
|
||||||
|
if (array_key_exists($key, $config)) {
|
||||||
|
$val = $config[$key];
|
||||||
|
if ($val >= 0 && $val <= 100) {
|
||||||
|
$this->cfg()->$key = floatval($val) / 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (["use_failed",] as $key) {
|
||||||
|
if (array_key_exists($key, $config)) {
|
||||||
|
$this->cfg()->$key = boolval($config[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the current configuration string.
|
||||||
|
* @return string Configuration string
|
||||||
|
*/
|
||||||
|
public function config_string() {
|
||||||
|
return json_encode([
|
||||||
|
"thresh_completed" => 100 * $this->cfg()->thresh_completed,
|
||||||
|
"use_failed" => $this->cfg()->use_failed,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if aggregation method wants to select gradables
|
||||||
|
* @return bool True if aggregation method needs gradables to be selected
|
||||||
|
*/
|
||||||
|
public function select_gradables() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if aggregation method is deprecated
|
||||||
|
* @return bool True if aggregation method is deprecated
|
||||||
|
*/
|
||||||
|
public function deprecated() {
|
||||||
|
return self::DEPRECATED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if Aggregation method makes use of "required grades" in a course/module.
|
||||||
|
* @return bool True if Aggregation method makes use of "required grades" in a course/module.
|
||||||
|
*/
|
||||||
|
public function use_required_grades() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if aggregation method makes use of required grades
|
||||||
|
* @return bool True if aggregation method makes use of
|
||||||
|
*/
|
||||||
|
public function use_item_conditions() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the aggregation method uses course competencies,
|
||||||
|
* @return bool True if the aggregation method uses course competencies,
|
||||||
|
*/
|
||||||
|
public function use_coursecompetencies() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return course completion information based on the core completion infromation
|
||||||
|
* Possible states:
|
||||||
|
* completion::EXCELLENT - Completed with excellent results
|
||||||
|
* completion::GOOD - Completed with good results
|
||||||
|
* completion::COMPLETED - Completed
|
||||||
|
* completion::PROGRESS - Started, but not completed yey
|
||||||
|
* completion::FAILED - Failed
|
||||||
|
* completion::INCOMPLETE - Not yet started
|
||||||
|
* @param courseinfo $courseinfo Courseinfo object for the course to check
|
||||||
|
* @param studyitem $studyitem Studyitem object for the course to check
|
||||||
|
* @param int $userid Id of user to check this course for
|
||||||
|
* @return int Aggregated completion as completion class constant
|
||||||
|
*/
|
||||||
|
public function aggregate_course(courseinfo $courseinfo, studyitem $studyitem, $userid) {
|
||||||
|
// Retrieve the course competencies
|
||||||
|
$course = $courseinfo->course();
|
||||||
|
$cci = new coursecompetencyinfo($course,$studyitem);
|
||||||
|
|
||||||
|
$competencies = $cci->course_competencies();
|
||||||
|
$count = 0;
|
||||||
|
$courseproficient = 0;
|
||||||
|
$proficient = 0;
|
||||||
|
foreach ($competencies as $c) {
|
||||||
|
$count += 1;
|
||||||
|
$p = $cci->proficiency($c,$userid);
|
||||||
|
if ($p->proficient) {
|
||||||
|
$proficient += 1;
|
||||||
|
}
|
||||||
|
if ($p->courseproficient) {
|
||||||
|
$courseproficient += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine minimum for
|
||||||
|
$limit = $this->cfg()->thresh_completed * $count;
|
||||||
|
$coursefinished = ($course->enddate) ? ($course->enddate < time()) : false;
|
||||||
|
|
||||||
|
if ($courseproficient >= $count) {
|
||||||
|
if ($limit < $count) {
|
||||||
|
return completion::EXCELLENT;
|
||||||
|
} else {
|
||||||
|
return completion::COMPLETED;
|
||||||
|
}
|
||||||
|
} else if ($courseproficient > $limit) {
|
||||||
|
return completion::COMPLETED;
|
||||||
|
} else if ($courseproficient > 0) {
|
||||||
|
if ( $this->cfg()->use_failed && $coursefinished) {
|
||||||
|
return completion::FAILED;
|
||||||
|
} else {
|
||||||
|
return completion::PROGRESS;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ( $this->cfg()->use_failed && $coursefinished) {
|
||||||
|
return completion::FAILED;
|
||||||
|
} else {
|
||||||
|
return completion::INCOMPLETE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aggregate juncton/filter inputs into one final junction outcome
|
||||||
|
* @param int[] $completion List of completion inputs
|
||||||
|
* @param studyitem $studyitem Studyitem object for the junction
|
||||||
|
* @param int $userid Id of user to check completion for
|
||||||
|
* @return int Aggregated completion as completion class constant
|
||||||
|
*/
|
||||||
|
public function aggregate_junction(array $completion, studyitem $studyitem, $userid = 0) {
|
||||||
|
// Aggregate multiple incoming states into one junction or finish.
|
||||||
|
// Possible states:.
|
||||||
|
// - completion::EXCELLENT - All incoming states are excellent.
|
||||||
|
// - completion::GOOD - All incoming states are at least good.
|
||||||
|
// - completion::COMPLETED - All incoming states are at least completed.
|
||||||
|
// - completion::FAILED - All incoming states are failed.
|
||||||
|
// - completion::INCOMPLETE - All incoming states are incomplete.
|
||||||
|
// - completion::PROGRESS - All other states.
|
||||||
|
|
||||||
|
$method = strtoupper($studyitem->conditions()); // One of ANY or ALL.
|
||||||
|
|
||||||
|
// First count all states.
|
||||||
|
$statecount = completion::count_states($completion);
|
||||||
|
$total = count($completion);
|
||||||
|
|
||||||
|
if ($method == "ANY") {
|
||||||
|
if ( $statecount[completion::EXCELLENT] >= 1 ) {
|
||||||
|
return completion::EXCELLENT;
|
||||||
|
} else if ( $statecount[completion::GOOD] >= 1 ) {
|
||||||
|
return completion::GOOD;
|
||||||
|
} else if ( $statecount[completion::COMPLETED] >= 1 ) {
|
||||||
|
return completion::COMPLETED;
|
||||||
|
} else if ( $statecount[completion::PROGRESS] >= 1 ) {
|
||||||
|
return completion::PROGRESS;
|
||||||
|
} else if ( $statecount[completion::FAILED] >= 1) {
|
||||||
|
return completion::FAILED;
|
||||||
|
} else {
|
||||||
|
return completion::INCOMPLETE;
|
||||||
|
}
|
||||||
|
} else { /* default value of ALL */
|
||||||
|
if ( $total == $statecount[completion::EXCELLENT]) {
|
||||||
|
return completion::EXCELLENT;
|
||||||
|
} else if ( $total == ( $statecount[completion::EXCELLENT]
|
||||||
|
+ $statecount[completion::GOOD]) ) {
|
||||||
|
return completion::GOOD;
|
||||||
|
} else if ( $total == ( $statecount[completion::EXCELLENT]
|
||||||
|
+ $statecount[completion::GOOD]
|
||||||
|
+ $statecount[completion::COMPLETED]) ) {
|
||||||
|
return completion::COMPLETED;
|
||||||
|
} else if ( $statecount[completion::FAILED]) {
|
||||||
|
return completion::FAILED;
|
||||||
|
} else if ( $total == $statecount[completion::INCOMPLETE]) {
|
||||||
|
return completion::INCOMPLETE;
|
||||||
|
} else {
|
||||||
|
return completion::PROGRESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine completion for a single grade and user
|
||||||
|
* @param gradeinfo $gradeinfo Gradeinfo object for grade to check
|
||||||
|
* @param mixed $userid Id of user to check completion for
|
||||||
|
* @return int Aggregated completion as completion class constant
|
||||||
|
*/
|
||||||
|
public function grade_completion(gradeinfo $gradeinfo, $userid) {
|
||||||
|
// COURSE COMPETENCIES DOESN'T REALLY USE THIS FUNCTION.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -97,7 +97,7 @@ class core_aggregator extends \local_treestudyplan\aggregator {
|
||||||
* Determine if the aggregation method uses core_completion, or treestudyplan custom completion.
|
* Determine if the aggregation method uses core_completion, or treestudyplan custom completion.
|
||||||
* @return bool True if the aggregation method uses core_completion
|
* @return bool True if the aggregation method uses core_completion
|
||||||
*/
|
*/
|
||||||
public function usecorecompletioninfo() {
|
public function use_corecompletioninfo() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,82 +217,6 @@ class core_aggregator extends \local_treestudyplan\aggregator {
|
||||||
*/
|
*/
|
||||||
public function grade_completion(gradeinfo $gradeinfo, $userid) {
|
public function grade_completion(gradeinfo $gradeinfo, $userid) {
|
||||||
// CORE COMPLETION DOESN'T REALLY USE THIS FUNCTION.
|
// CORE COMPLETION DOESN'T REALLY USE THIS FUNCTION.
|
||||||
|
|
||||||
global $DB;
|
|
||||||
$table = "local_treestudyplan_gradecfg";
|
|
||||||
$gradeitem = $gradeinfo->get_gradeitem();
|
|
||||||
$grade = $gradeitem->get_final($userid);
|
|
||||||
|
|
||||||
if (empty($grade)) {
|
|
||||||
return completion::INCOMPLETE;
|
|
||||||
} else if ($grade->finalgrade === null) {
|
|
||||||
// On assignments, grade NULL means a submission has not yet been graded,.
|
|
||||||
// But on quizes this can also mean a quiz might have been started.
|
|
||||||
// Therefor, we treat a NULL result as a reason to check the relevant gradingscanner for presence of pending items.
|
|
||||||
|
|
||||||
// Since we want old results to be visible until a pending item was graded, we only use this state here.
|
|
||||||
// Pending items are otherwise expressly indicated by the "pendingsubmission" field in the user model.
|
|
||||||
if ($gradeinfo->get_gradingscanner()->pending($userid)) {
|
|
||||||
return completion::PENDING;
|
|
||||||
} else {
|
|
||||||
return completion::INCOMPLETE;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$grade = $gradeitem->get_final($userid);
|
|
||||||
// First determine if we have a grade_config for this scale or this maximum grade.
|
|
||||||
$finalgrade = $grade->finalgrade;
|
|
||||||
$scale = $gradeinfo->get_scale();
|
|
||||||
if ( isset($scale)) {
|
|
||||||
$gradecfg = $DB->get_record($table, ["scale_id" => $scale->id]);
|
|
||||||
} else if ($gradeitem->grademin == 0) {
|
|
||||||
$gradecfg = $DB->get_record($table, ["grade_points" => $gradeitem->grademax]);
|
|
||||||
} else {
|
|
||||||
$gradecfg = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// For point grades, a provided grade pass overrides the defaults in the gradeconfig.
|
|
||||||
// For scales, the configuration in the gradeconfig is leading.
|
|
||||||
|
|
||||||
if ($gradecfg && (isset($scale) || $gradeitem->gradepass == 0)) {
|
|
||||||
// If so, we need to know if the grade is .
|
|
||||||
if ($finalgrade >= $gradecfg->min_completed) {
|
|
||||||
// Return completed if completed.
|
|
||||||
return completion::COMPLETED;
|
|
||||||
} else if ($this->use_failed && $finalgrade < $gradecfg->min_progress) {
|
|
||||||
// Return failed if failed is enabled and the grade is less than the minimum grade for progress.
|
|
||||||
return completion::FAILED;
|
|
||||||
} else {
|
|
||||||
return completion::PROGRESS;
|
|
||||||
}
|
|
||||||
} else if ($gradeitem->gradepass > 0) {
|
|
||||||
$range = floatval($gradeitem->grademax - $gradeitem->grademin);
|
|
||||||
// If no gradeconfig and gradepass is set, use that one to determine config.
|
|
||||||
if ($finalgrade >= $gradeitem->gradepass) {
|
|
||||||
return completion::COMPLETED;
|
|
||||||
} else if ($this->use_failed && $gradeitem->gradepass >= 3 && $range >= 3 && $finalgrade == 1) {
|
|
||||||
// Return failed if failed is enabled and the grade is 1, while there are at leas 3 states.
|
|
||||||
return completion::FAILED;
|
|
||||||
} else {
|
|
||||||
return completion::PROGRESS;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Blind assumptions if nothing is provided.
|
|
||||||
// Over 55% of range is completed.
|
|
||||||
// If range >= 3 and failed is enabled, assume that this means failed.
|
|
||||||
$g = floatval($finalgrade - $gradeitem->grademin);
|
|
||||||
$range = floatval($gradeitem->grademax - $gradeitem->grademin);
|
|
||||||
$score = $g / $range;
|
|
||||||
|
|
||||||
if ($score > 0.55) {
|
|
||||||
return completion::COMPLETED;
|
|
||||||
} else if ($this->use_failed && $range >= 3 && $finalgrade == 1) {
|
|
||||||
// Return failed if failed is enabled and the grade is 1, while there are at leas 3 states.
|
|
||||||
return completion::FAILED;
|
|
||||||
} else {
|
|
||||||
return completion::PROGRESS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,18 +53,10 @@ class tristate_aggregator extends \local_treestudyplan\aggregator {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if Aggregation method makes use of "required grades" in a course/module.
|
* Determine if the aggregation method uses manual activity selection,
|
||||||
* @return bool True if Aggregation method makes use of "required grades" in a course/module.
|
* @return bool True if the aggregation method uses manual activity selection
|
||||||
*/
|
*/
|
||||||
public function use_required_grades() {
|
public function use_manualactivityselection() {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine if aggregation method makes use of required grades
|
|
||||||
* @return bool True if aggregation method makes use of
|
|
||||||
*/
|
|
||||||
public function use_item_conditions() {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -207,8 +207,7 @@ class studentstudyplanservice extends \external_api {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate context now.
|
// Do not validate the context in this instance to avoid requiring logins when this is not wanted
|
||||||
\external_api::validate_context(\context_system::instance());
|
|
||||||
|
|
||||||
$userid = $invite->user_id;
|
$userid = $invite->user_id;
|
||||||
|
|
||||||
|
@ -262,8 +261,7 @@ class studentstudyplanservice extends \external_api {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate context now.
|
// Do not validate the context in this instance to avoid requiring logins when this is not wanted
|
||||||
\external_api::validate_context(\context_system::instance());
|
|
||||||
|
|
||||||
$studyplan = studyplan::find_by_id($studyplanid);
|
$studyplan = studyplan::find_by_id($studyplanid);
|
||||||
$userid = $invite->user_id;
|
$userid = $invite->user_id;
|
||||||
|
@ -317,8 +315,7 @@ class studentstudyplanservice extends \external_api {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate context now.
|
// Do not validate the context in this instance to avoid requiring logins when this is not wanted
|
||||||
\external_api::validate_context(\context_system::instance());
|
|
||||||
|
|
||||||
$page = studyplanpage::find_by_id($pageid);
|
$page = studyplanpage::find_by_id($pageid);
|
||||||
$studyplan = $page->studyplan();
|
$studyplan = $page->studyplan();
|
||||||
|
|
|
@ -229,7 +229,7 @@ class studyitem {
|
||||||
if ($mode == "export") {
|
if ($mode == "export") {
|
||||||
$model['course'] = $ci->shortname();
|
$model['course'] = $ci->shortname();
|
||||||
} else {
|
} else {
|
||||||
$model['course'] = $ci->editor_model($this->aggregator->usecorecompletioninfo());
|
$model['course'] = $ci->editor_model();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -501,7 +501,7 @@ class studyitem {
|
||||||
// Add course if available.
|
// Add course if available.
|
||||||
if (courseinfo::exists($this->r->course_id)) {
|
if (courseinfo::exists($this->r->course_id)) {
|
||||||
$cinfo = $this->getcourseinfo();
|
$cinfo = $this->getcourseinfo();
|
||||||
$model['course'] = $cinfo->user_model($userid, $this->aggregator->usecorecompletioninfo());
|
$model['course'] = $cinfo->user_model($userid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add incoming and outgoing connection info.
|
// Add incoming and outgoing connection info.
|
||||||
|
|
|
@ -266,25 +266,35 @@ $string["bistate_aggregator_desc"] = 'Outcomes are completed or not (e.g. not st
|
||||||
$string["core_aggregator_title"] = 'Moodle course completion';
|
$string["core_aggregator_title"] = 'Moodle course completion';
|
||||||
$string["core_aggregator_desc"] = 'Use Moodle core completion';
|
$string["core_aggregator_desc"] = 'Use Moodle core completion';
|
||||||
|
|
||||||
|
$string["competency_aggregator_title"] = 'Course competencies';
|
||||||
|
$string["competency_aggregator_desc"] = 'Use the competencies linekd to the course';
|
||||||
|
|
||||||
$string["setting_bistate_heading"] = 'Defaults for Completed + Required outcomes';
|
$string["setting_bistate_heading"] = 'Defaults for Completed + Required outcomes';
|
||||||
$string["settingdesc_bistate_heading"] = 'Set the defaults for this aggregation method';
|
$string["settingdesc_bistate_heading"] = 'Set the defaults for this aggregation method';
|
||||||
|
|
||||||
$string["choose_aggregation_style"] = 'Choose aggregation style';
|
$string["choose_aggregation_style"] = 'Choose aggregation style';
|
||||||
|
|
||||||
$string["select_scaleitem"] = 'Choose...';
|
$string["select_scaleitem"] = 'Choose...';
|
||||||
$string["setting_bistate_thresh_excellent"] = 'Threshold for excellent';
|
$string["setting_bistate_thresh_excellent"] = 'Threshold for excellent (%)';
|
||||||
$string["settingdesc_bistate_thresh_excellent"] = 'Minimum percentage of outcomes completed for result "Excellent"';
|
$string["settingdesc_bistate_thresh_excellent"] = 'Minimum percentage of outcomes completed for result "Excellent"';
|
||||||
$string["setting_bistate_thresh_good"] = 'Threshold for good';
|
$string["setting_bistate_thresh_good"] = 'Threshold for good (%)';
|
||||||
$string["settingdesc_bistate_thresh_good"] = 'Minimum percentage of outcomes completed for result "Good"';
|
$string["settingdesc_bistate_thresh_good"] = 'Minimum percentage of outcomes completed for result "Good"';
|
||||||
$string["setting_bistate_thresh_completed"] = 'Threshold for completed';
|
$string["setting_bistate_thresh_completed"] = 'Threshold for completed (%)';
|
||||||
$string["settingdesc_bistate_thresh_completed"] = 'Minimum percentage of outcomes completed for result "Completed"';
|
$string["settingdesc_bistate_thresh_completed"] = 'Minimum percentage of outcomes completed for result "Completed"';
|
||||||
$string["setting_bistate_support_failed"] = 'Support "Failed" result';
|
$string["setting_bistate_support_failed"] = 'Support "Failed" result';
|
||||||
$string["settingdesc_bistate_support_failed"] = 'Whether the result "Failed" is supported or not';
|
$string["settingdesc_bistate_support_failed"] = 'When the course end date has passed, mark course as "Failed" instead of "Progress"';
|
||||||
$string["setting_bistate_thresh_progress"] = 'Threshold for progress';
|
$string["setting_bistate_thresh_progress"] = 'Threshold for progress (%)';
|
||||||
$string["settingdesc_bistate_thresh_progress"] = 'Minimum percentage of outcomes that should not be failed in order to qualify for progress result. <br><strong>Only relevant if "Failed" results are supported</strong>';
|
$string["settingdesc_bistate_thresh_progress"] = 'Minimum percentage of outcomes that should not be failed in order to qualify for progress result. <br><strong>Only relevant if "Failed" results are supported</strong>';
|
||||||
$string["setting_bistate_accept_pending_submitted"] = 'Accept submitted but ungraded result as "progress"';
|
$string["setting_bistate_accept_pending_submitted"] = 'Accept submitted but ungraded result as "progress"';
|
||||||
$string["settingdesc_bistate_accept_pending_submitted"] = 'If enabled, submitted but ungraded outcomes will still count toward progress. If disabled, only graded outcomes will count';
|
$string["settingdesc_bistate_accept_pending_submitted"] = 'If enabled, submitted but ungraded outcomes will still count toward progress. If disabled, only graded outcomes will count';
|
||||||
|
|
||||||
|
$string["setting_competency_heading"] = 'Defults for course competencies ';
|
||||||
|
$string["settingdesc_competency_heading"] = 'Set the defaults for this aggregation method';
|
||||||
|
$string["setting_competency_thresh_completed"] = 'Threshold for good (%)';
|
||||||
|
$string["settingdesc_competency_thresh_completed"] = 'Minimum percentage of proficient competencies for result "Completed';
|
||||||
|
$string["setting_competency_support_failed"] = 'Support "Failed" result';
|
||||||
|
$string["settingdesc_competency_support_failed"] = 'When the course end date has passed, mark course as "Failed" instead of "Progress"';
|
||||||
|
|
||||||
$string["grade_include"] = "Include";
|
$string["grade_include"] = "Include";
|
||||||
$string["grade_require"] = "Require";
|
$string["grade_require"] = "Require";
|
||||||
$string["required_goal"] = "Required outcome";
|
$string["required_goal"] = "Required outcome";
|
||||||
|
|
|
@ -263,6 +263,10 @@ $string["bistate_aggregator_desc"] = 'Doelen zijn behaald of niet (o.a. niet ges
|
||||||
$string["core_aggregator_title"] = 'Moodle cursusvoltooiing';
|
$string["core_aggregator_title"] = 'Moodle cursusvoltooiing';
|
||||||
$string["core_aggregator_desc"] = 'Gebruik de ingesteld cursusvoltooiing';
|
$string["core_aggregator_desc"] = 'Gebruik de ingesteld cursusvoltooiing';
|
||||||
|
|
||||||
|
$string["competency_aggregator_title"] = 'Curuscompetenties';
|
||||||
|
$string["competency_aggregator_desc"] = 'Gebruik de bij de cursus ingestelde competenties';
|
||||||
|
|
||||||
|
|
||||||
$string["setting_bistate_heading"] = 'Standaardwaarden voor Behaald + Vereidte leerdoelen ';
|
$string["setting_bistate_heading"] = 'Standaardwaarden voor Behaald + Vereidte leerdoelen ';
|
||||||
$string["settingdesc_bistate_heading"] = 'Stel de standaardwaarden in voor deze verzamelmethode';
|
$string["settingdesc_bistate_heading"] = 'Stel de standaardwaarden in voor deze verzamelmethode';
|
||||||
|
|
||||||
|
@ -271,11 +275,11 @@ $string["choose_aggregation_style"] = 'Kies berekening van eindresultaten';
|
||||||
$string["select_scaleitem"] = 'Kies...';
|
$string["select_scaleitem"] = 'Kies...';
|
||||||
|
|
||||||
$string["setting_bistate_thresh_excellent"] = 'Drempelwaarde voor uitstekend (%)';
|
$string["setting_bistate_thresh_excellent"] = 'Drempelwaarde voor uitstekend (%)';
|
||||||
$string["settingdesc_bistate_thresh_excellent"] = 'Minimumpercentage of goals completed for result "Excellent"';
|
$string["settingdesc_bistate_thresh_excellent"] = 'Minimumpercentage behaalde doelen voor "Uitstekend"';
|
||||||
$string["setting_bistate_thresh_good"] = 'Drempelwaarde voor goed (%)';
|
$string["setting_bistate_thresh_good"] = 'Drempelwaarde voor goed (%)';
|
||||||
$string["settingdesc_bistate_thresh_good"] = 'Minimum percentage of goals completed for result "Good"';
|
$string["settingdesc_bistate_thresh_good"] = 'Minimum ercentage behaalde doelen voor "Goed"';
|
||||||
$string["setting_bistate_thresh_completed"] = 'Drempelwaarde voor voltooid (%)';
|
$string["setting_bistate_thresh_completed"] = 'Drempelwaarde voor voltooid (%)';
|
||||||
$string["settingdesc_bistate_thresh_completed"] = 'Minimumpercentage of goals completed for result "Completed"';
|
$string["settingdesc_bistate_thresh_completed"] = 'Minimumpercentage behaalde doelen voor "Voltooid"';
|
||||||
$string["setting_bistate_support_failed"] = 'Onvoldoende ingeschakeld';
|
$string["setting_bistate_support_failed"] = 'Onvoldoende ingeschakeld';
|
||||||
$string["settingdesc_bistate_support_failed"] = 'Vink aan om "Onvoldoende" weer te kunnen geven als resultaat voor een module';
|
$string["settingdesc_bistate_support_failed"] = 'Vink aan om "Onvoldoende" weer te kunnen geven als resultaat voor een module';
|
||||||
$string["setting_bistate_thresh_progress"] = 'Drempelwaarde voor "in ontwikkeling" (%)';
|
$string["setting_bistate_thresh_progress"] = 'Drempelwaarde voor "in ontwikkeling" (%)';
|
||||||
|
@ -283,6 +287,13 @@ $string["settingdesc_bistate_thresh_progress"] = 'Minimumpercentage van doelen d
|
||||||
$string["setting_bistate_accept_pending_submitted"] = 'Accepteer nog niet beoordeelde doelen als "in ontwikkeling"';
|
$string["setting_bistate_accept_pending_submitted"] = 'Accepteer nog niet beoordeelde doelen als "in ontwikkeling"';
|
||||||
$string["settingdesc_bistate_accept_pending_submitted"] = 'Neem leerdoelen waarbij bewijs is ingeleverd, maar wat nog niet is beoordeeld mee als "in ontwikkeling", zolang er nog geen beoordeling is';
|
$string["settingdesc_bistate_accept_pending_submitted"] = 'Neem leerdoelen waarbij bewijs is ingeleverd, maar wat nog niet is beoordeeld mee als "in ontwikkeling", zolang er nog geen beoordeling is';
|
||||||
|
|
||||||
|
$string["setting_competency_heading"] = 'Standaardwaarden voor cursuscompetenties ';
|
||||||
|
$string["settingdesc_competency_heading"] = 'Stel de standaardwaarden in voor deze verzamelmethode';
|
||||||
|
$string["setting_competency_thresh_completed"] = 'Drempelwaarde voor voltooid (%)';
|
||||||
|
$string["settingdesc_competency_thresh_completed"] = 'Minimumpercentage behaalde competenties voor "Behaald"';
|
||||||
|
$string["setting_competency_support_failed"] = 'Onvoldoende ingeschakeld';
|
||||||
|
$string["settingdesc_competency_support_failed"] = 'Vink aan om "Onvoldoende" weer te kunnen geven als eind resultaat voor een cursus';
|
||||||
|
|
||||||
$string["grade_include"] = "Doel";
|
$string["grade_include"] = "Doel";
|
||||||
$string["grade_require"] = "Verplicht";
|
$string["grade_require"] = "Verplicht";
|
||||||
$string["required_goal"] = "Verplicht leerdoel";
|
$string["required_goal"] = "Verplicht leerdoel";
|
||||||
|
|
18
settings.php
18
settings.php
|
@ -107,6 +107,24 @@ if ($hassiteconfig) {
|
||||||
$displayfields
|
$displayfields
|
||||||
));
|
));
|
||||||
|
|
||||||
|
// BISTATE AGGREGATON DEFAULTS.
|
||||||
|
$page->add(new admin_setting_heading('local_treestudyplan/competency_aggregation_heading',
|
||||||
|
get_string('setting_competency_heading', 'local_treestudyplan'),
|
||||||
|
get_string('settingdesc_competency_heading', 'local_treestudyplan')
|
||||||
|
));
|
||||||
|
$page->add(new admin_setting_configtext('local_treestudyplan/competency_thresh_completed',
|
||||||
|
get_string('setting_competency_thresh_completed', 'local_treestudyplan'),
|
||||||
|
get_string('settingdesc_competency_thresh_completed', 'local_treestudyplan'),
|
||||||
|
"66",
|
||||||
|
PARAM_INT
|
||||||
|
));
|
||||||
|
|
||||||
|
$page->add(new admin_setting_configcheckbox('local_treestudyplan/competency_support_failed',
|
||||||
|
get_string('setting_competency_support_failed', 'local_treestudyplan'),
|
||||||
|
get_string('settingdesc_competency_support_failed', 'local_treestudyplan'),
|
||||||
|
true,
|
||||||
|
));
|
||||||
|
|
||||||
// BISTATE AGGREGATON DEFAULTS.
|
// BISTATE AGGREGATON DEFAULTS.
|
||||||
$page->add(new admin_setting_heading('local_treestudyplan/bistate_aggregation_heading',
|
$page->add(new admin_setting_heading('local_treestudyplan/bistate_aggregation_heading',
|
||||||
get_string('setting_bistate_heading', 'local_treestudyplan'),
|
get_string('setting_bistate_heading', 'local_treestudyplan'),
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
defined('MOODLE_INTERNAL') || die();
|
defined('MOODLE_INTERNAL') || die();
|
||||||
|
|
||||||
$plugin->component = 'local_treestudyplan'; // Recommended since 2.0.2 (MDL-26035). Required since 3.0 (MDL-48494).
|
$plugin->component = 'local_treestudyplan'; // Recommended since 2.0.2 (MDL-26035). Required since 3.0 (MDL-48494).
|
||||||
$plugin->version = 2023111202; // YYYYMMDDHH (year, month, day, iteration).
|
$plugin->version = 2023111700; // YYYYMMDDHH (year, month, day, iteration).
|
||||||
$plugin->requires = 2021051700; // YYYYMMDDHH (This is the release version for Moodle 3.11).
|
$plugin->requires = 2021051700; // YYYYMMDDHH (This is the release version for Moodle 3.11).
|
||||||
|
|
||||||
$plugin->release = "1.1.0-b";
|
$plugin->release = "1.1.0-b";
|
||||||
|
|
Loading…
Reference in New Issue
Block a user