From 5d12a6c6535ded0ec9fbe54a854ca121dac3276e Mon Sep 17 00:00:00 2001 From: PMKuipers Date: Mon, 29 May 2023 23:00:45 +0200 Subject: [PATCH] Added corecompletion detailinfo for teacher/editor --- amd/src/report-viewer-components.js | 407 +++++++++++++++++++++------- classes/corecompletioninfo.php | 146 ++++++++-- 2 files changed, 436 insertions(+), 117 deletions(-) diff --git a/amd/src/report-viewer-components.js b/amd/src/report-viewer-components.js index daf14f0..754fdb5 100644 --- a/amd/src/report-viewer-components.js +++ b/amd/src/report-viewer-components.js @@ -163,6 +163,7 @@ export default { class='r-report-tabs'> @@ -1106,10 +1066,11 @@ export default {

{{ value.course.fullname }} - +

{{ value.course.context.path.join(" / ")}}
@@ -1125,51 +1086,65 @@ export default { - - - - - - -
{{g.name}} - - - - - -
+ + + + + `, + }); - + @@ -1180,7 +1155,7 @@ export default { {{text.grade_include}}{{text.grade_require}} -
  • +
  • @@ -1190,20 +1165,270 @@ export default { {{g.name}}
  • -
    +
    + `, + }); + + + + //TODO: Selected activities dispaly + Vue.component('r-item-teachergrades',{ + props: { + value : { + type: Object, + default: function(){ return {};}, + }, + useRequiredGrades: { + type: Boolean, + default: false, + }, + }, + data() { + return { + txt: { + grading: strings.grading, + } + }; + }, + computed: { + pendingsubmission(){ + let result = false; + for(const ix in this.value.grades){ + const g = this.value.grades[ix]; + if(g.pendingsubmission){ + result = true; + break; + } + } + return result; + }, + filtered_grades(){ + return this.value.grades.filter(g => g.selected); + }, + }, + methods: { + determine_grading_icon(gradingstate){ + switch(gradingstate){ + default: // "nogrades": + return "circle-o"; + case "ungraded": + return "exclamation-circle"; + case "unknown": + return "question-circle-o"; + case "graded": + return "check"; + case "allgraded": + return "check"; + case "unsubmitted": + return "dot-circle-o"; + } + }, + grading_icon(grade){ + return this.determine_grading_icon(this.is_grading_needed(grade)); + }, + is_grading_needed(grade){ + if(grade.grading){ + if(grade.grading.ungraded){ + return 'ungraded'; + } + else if(grade.grading.graded){ + if(Number(grade.grading.graded) == Number(grade.grading.students)){ + return 'allgraded'; + } + else { + return 'graded'; + } + } + else { + return 'unsubmitted'; + } + } else { + return 'unknown'; + } + }, + includeChanged(newValue,g){ + call([{ + methodname: 'local_treestudyplan_include_grade', + args: { 'grade_id': g.id, + 'item_id': this.value.id, + 'include': newValue, + 'required': g.required, + } + }])[0].fail(notification.exception); + }, + requiredChanged(newValue,g){ + call([{ + methodname: 'local_treestudyplan_include_grade', + args: { 'grade_id': g.id, + 'item_id': this.value.id, + 'include': g.selected, + 'required': newValue, + } + }])[0].fail(notification.exception); + }, + }, + template: ` +
    + + + + + + +
    {{g.name}} + + + + + +
    +
    + `, + }); + + //TODO: Core completion version of student course info + Vue.component('r-item-teachercompletion',{ + props: { + value : { + type: Object, + default: function(){ return {};}, + }, + guestmode: { + type: Boolean, + default: false, + }, + course: { + type: Object, + default: function(){ return {};}, + }, + }, + data() { + return { + text: { + completion_incomplete: "completion_incomplete", + completion_failed: "completion_failed", + completion_pending: "completion_pending", + completion_progress: "completion_progress", + completion_completed: "completion_completed", + completion_good: "completion_good", + completion_excellent: "completion_excellent", + view_feedback: "view_feedback", + coursetiming_past: "coursetiming_past", + coursetiming_present: "coursetiming_present", + coursetiming_future: "coursetiming_future", + required_goal: "required_goal", + }, + }; + }, + 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: { + }, + 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'; + } + }, + template: ` + + +
    + `, + }); - - `, - }); Vue.component('r-grading-bar',{ props: { diff --git a/classes/corecompletioninfo.php b/classes/corecompletioninfo.php index 7df0793..f17c59d 100644 --- a/classes/corecompletioninfo.php +++ b/classes/corecompletioninfo.php @@ -55,11 +55,12 @@ class corecompletioninfo { return new \external_single_structure([ "title" => new \external_value(PARAM_TEXT,'name of subitem',VALUE_OPTIONAL), "link" => new \external_value(PARAM_TEXT, 'optional link to more details',VALUE_OPTIONAL), - // ADD BELOW IF NEEDED - try using name, description and link fields first - /* - "required_grade" => new \external_value(PARAM_TEXT, 'required_grade',VALUE_OPTIONAL), - "course_link" => course_info::simple_structure(VALUE_OPTIONAL), - */ + "details" => new \external_single_structure([ + "type" => new \external_value(PARAM_RAW, 'type',VALUE_OPTIONAL), + "criteria" => new \external_value(PARAM_RAW, 'criteria',VALUE_OPTIONAL), + "requirement" => new \external_value(PARAM_RAW, 'requirement',VALUE_OPTIONAL), + "status" => new \external_value(PARAM_RAW, 'status',VALUE_OPTIONAL), + ]), ], 'completion type',$value); } @@ -92,11 +93,6 @@ class corecompletioninfo { "status" => new \external_value(PARAM_RAW, 'status',VALUE_OPTIONAL), ]), "link" => new \external_value(PARAM_TEXT, 'optional link to more details',VALUE_OPTIONAL), - // ADD BELOW IF NEEDED - try using name, description and link fields first - /* - "required_grade" => new \external_value(PARAM_TEXT, 'required_grade',VALUE_OPTIONAL), - "course_link" => course_info::simple_structure(VALUE_OPTIONAL), - */ "completed" => new \external_value(PARAM_BOOL, 'simple completed or not'), "status" => new \external_value(PARAM_TEXT, 'extended completion status ["incomplete","progress","complete", "complete-pass","complete-fail"]'), "pending" => new \external_value(PARAM_BOOL, 'optional pending state, for submitted but not yet reviewed activities',VALUE_OPTIONAL), @@ -164,28 +160,126 @@ class corecompletioninfo { ]; foreach($criterias as $criteria){ - $iinfo = [ - "title" => $criteria->get_title_detailed(), - ]; - - //TODO: MAKE SURE THIS DATA IS FILLED - - if($type == COMPLETION_CRITERIA_TYPE_ACTIVITY){ - // If it's an activity completion, add the relevant activity - //$cm = $this->modinfo->get_cm($criterias->moduleinstance); - // retrieve data for this object - //$data = $completion->get_data($cm, false, $userid); + // Unfortunately, we cannot easily get the criteria details with get_details() without having a + // user completion object involved, so'we'll have to retrieve the details per completion type + // See moodle/completion/criteria/completion_criteria_*.php::get_details() for the code that is + // in the code below is based on + + if($type == COMPLETION_CRITERIA_TYPE_SELF){ + $details = [ + "type" => $criteria->get_title(), + "criteria" => $criteria->get_title(), + "requirement" => get_string('markingyourselfcomplete', 'completion'), + "status" => "", + ]; } - else if ($type == COMPLETION_CRITERIA_TYPE_COURSE){ - // If it's a (sub) course dependency, add the course as a link + else if ($type == COMPLETION_CRITERIA_TYPE_DATE){ + $details = [ + "type" => get_string('datepassed', 'completion'), + "criteria" => get_string('remainingenroleduntildate', 'completion'), + "requirement" => userdate($criteria->timeend, '%d %B %Y'), + "status" => "", + ]; + } + else if ($type == COMPLETION_CRITERIA_TYPE_UNENROL){ + $details = [ + "type" => get_string('unenrolment', 'completion'), + "criteria" => get_string('unenrolment', 'completion'), + "requirement" => get_string('unenrolingfromcourse', 'completion'), + "status" => "", + ]; + } + else if ($type == COMPLETION_CRITERIA_TYPE_ACTIVITY){ + $cm = $this->modinfo->get_cm($criteria->moduleinstance); + $details = [ + "type" => $criteria->get_title(), + "criteria" => "", // Will be built in a moment by code copied from completion_criteria_activity.php + "requirement" => "", // Will be built momentarily by code copied from completion_criteria_activity.php + "status" => "", + ]; + if ($cm->has_view()) { + $details['criteria'] = \html_writer::link($cm->url, $cm->get_formatted_name()); + } else { + $details['criteria'] = $cm->get_formatted_name(); + } + // Build requirements + $details['requirement'] = array(); + if ($cm->completion == COMPLETION_TRACKING_MANUAL) { + $details['requirement'][] = get_string('markingyourselfcomplete', 'completion'); + } elseif ($cm->completion == COMPLETION_TRACKING_AUTOMATIC) { + if ($cm->completionview) { + $modulename = core_text::strtolower(get_string('modulename', $criteria->module)); + $details['requirement'][] = get_string('viewingactivity', 'completion', $modulename); + } + + if (!is_null($cm->completiongradeitemnumber)) { + $details['requirement'][] = get_string('achievinggrade', 'completion'); + } + + if ($cm->completionpassgrade) { + $details['requirement'][] = get_string('achievingpassinggrade', 'completion'); + } + } + + $details['requirement'] = implode(', ', $details['requirement']); + + } + else if ($type == COMPLETION_CRITERIA_TYPE_DURATION){ + $details = [ + "type" => get_string('periodpostenrolment', 'completion'), + "criteria" => get_string('remainingenroledfortime', 'completion'), + "requirement" => get_string('xdays', 'completion', ceil($criteria->enrolperiod / (60*60*24))), + "status" => "", + ]; + } + else if ($type == COMPLETION_CRITERIA_TYPE_GRADE){ + $details = [ + "type" => get_string('coursegrade', 'completion'), + "criteria" => get_string('graderequired', 'completion'), + // TODO: convert to selected representation (letter, percentage, etc) + "requirement" => format_float($criteria->gradepass, $decimalpoints), + "status" => "", + ]; } else if ($type == COMPLETION_CRITERIA_TYPE_ROLE){ - // If it needs approval by a role, it probably already is in the title - + $criteria = $criteria->get_title(); + + $details = [ + "type" => get_string('manualcompletionby', 'completion'), + "criteria" => $criteria, + "requirement" => get_string('markedcompleteby', 'completion', $criteria), + "status" => "", + ]; } + else if ($type == COMPLETION_CRITERIA_TYPE_COURSE){ + $prereq = get_course($criteria->courseinstance); + $coursecontext = \context_course::instance($prereq->id, MUST_EXIST); + $fullname = format_string($prereq->fullname, true, array('context' => $coursecontext)); + $details = [ + "type" => $criteria->get_title(), + "criteria" => ''.s($fullname).'', + "requirement" => get_string('coursecompleted', 'completion'), + "status" => "", + ]; + } else { + // Moodle added a criteria type + $details = [ + "type" => "", + "criteria" => "", + "requirement" => "", + "status" => "", + ]; + } + + // only add the items list if we actually have items... - $cinfo["items"][] = $iinfo; + $cinfo["items"][] = [ + "id" => $criteria->id, + "title" => $criteria->get_title_detailed(), + "details" => $details, + ]; + } $info['conditions'][] = $cinfo;