Implemented teacher view for competencies
This commit is contained in:
		
							parent
							
								
									456e9b503e
								
							
						
					
					
						commit
						96caeb895a
					
				
					 16 changed files with 233 additions and 181 deletions
				
			
		
							
								
								
									
										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/report-viewer-components.min.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								amd/build/report-viewer-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
											
										
									
								
							
							
								
								
									
										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
											
										
									
								
							|  | @ -64,12 +64,13 @@ export function init(contextid,categoryid,options) { | |||
|         if ( !options.defaultAggregation ) { | ||||
|             options.defaultAggregation = "core"; | ||||
|         } | ||||
|         if ( !options.editMode ) { | ||||
|             options.editMode = false; | ||||
|         } | ||||
|     } else { | ||||
|         options = { defaultAggregation: "core"}; | ||||
|         options = { defaultAggregation: "core", editMode: false}; | ||||
|     } | ||||
| 
 | ||||
|     const in_systemcontext = (contextid <= 1); | ||||
| 
 | ||||
|     // Setup the initial Vue app for this page
 | ||||
|     let app = new Vue({ | ||||
|         el: '#root', | ||||
|  | @ -100,6 +101,7 @@ export function init(contextid,categoryid,options) { | |||
|             courses: [], | ||||
|             text: strings.studyplan, | ||||
|             usedcontexts: [], | ||||
|             initialEditMode: !!options.editMode, | ||||
|         }, | ||||
|         created() { | ||||
|             this.$root.$on('studyplanRemoved',(studyplan)=>{ | ||||
|  |  | |||
|  | @ -16,8 +16,8 @@ import {svgarcpath} from './util/svgarc'; | |||
| import Debugger from './util/debugger'; | ||||
| import Config from 'core/config'; | ||||
| import {ProcessStudyplan, ProcessStudyplanPage, objCopy} from './studyplan-processor'; | ||||
| 
 | ||||
| import TSComponents from './treestudyplan-components'; | ||||
| import {eventTypes as editSwEventTypes} from 'core/edit_switch'; | ||||
| 
 | ||||
| // Make π available as a constant
 | ||||
| const π = Math.PI; | ||||
|  | @ -180,6 +180,13 @@ export default { | |||
|         // Create new eventbus for interaction between item components
 | ||||
|         const ItemEventBus = new Vue(); | ||||
| 
 | ||||
|         // Add event listener for the edit mode event so we can react to it, or at the very least ignore it
 | ||||
|         document.addEventListener(editSwEventTypes.editModeSet,(e) => { | ||||
|             e.preventDefault(); | ||||
|             ItemEventBus.$emit('editModeSet',e.detail.editMode); | ||||
|         }); | ||||
| 
 | ||||
| 
 | ||||
|         Vue.component('r-progress-circle',{ | ||||
|             props: { | ||||
|                 value: { | ||||
|  | @ -1606,11 +1613,15 @@ export default { | |||
|                 </tr> | ||||
|                 <template v-else> | ||||
|                     <tr v-for='c in value.competencies'> | ||||
|                         <td :colspan="(c.details)?1:2"> | ||||
|                         <td> | ||||
|                             <a href="#" v-b-modal="'modal-competency-id-'+c.id"><span v-html='c.title'></span></a> | ||||
|                         </td> | ||||
|                         <td class='details' v-if="c.details"> | ||||
|                             <a href="#" v-b-modal="'modal-competency-id-'+c.id"><span v-html='c.details'></span></a> | ||||
|                         <td class='details' > | ||||
|                             <a v-if="c.details" href="#" v-b-modal="'modal-competency-id-'+c.id"><span v-html='c.details'></span></a> | ||||
|                             <abbr v-if="c.required" :title="text.required" | ||||
|                                 :class="'s-required ' + + completion_tag(c)" | ||||
|                                 ><i class='fa fa-asterisk' ></i | ||||
|                             ></abbr>     | ||||
|                         </td> | ||||
|                         <td :colspan="(c.required)?1:2"> | ||||
|                             <span :class="'r-completion-'+completion_tag(c)"> | ||||
|  | @ -1630,9 +1641,6 @@ export default { | |||
|                                 </template> | ||||
|                             </span> | ||||
|                         </td> | ||||
|                         <td v-if="c.required"> | ||||
|                             <span class="text-danger" v-if="c.required">{{ text.required }}</span> | ||||
|                         </td> | ||||
|                         <td v-if="c.feedback"> | ||||
|                             <a  v-b-modal="'r-competency-feedback-'+c.id" | ||||
|                                 href="#" | ||||
|  | @ -1675,17 +1683,22 @@ export default { | |||
|                                         <th colspan="3">{{text.results}}</th> | ||||
|                                     </tr> | ||||
|                                     <tr v-for="cc in c.children"> | ||||
|                                         <td :colspan="(c.details)?1:2" > | ||||
|                                         <td > | ||||
|                                             <a :href='competencyurl(c)' target="_blank"><span v-html='cc.title'></span></a> | ||||
|                                         </td> | ||||
|                                         <td class='details' v-if="cc.details"> | ||||
|                                             <a :href='competencyurl(c)' target="_blank"><span v-html='cc.details'></span></a> | ||||
|                                         <td class='details'> | ||||
|                                             <a v-if="cc.details" :href='competencyurl(c)' target="_blank"><span v-html='cc.details'></span></a> | ||||
|                                             <abbr v-if="c.required" :title="text.required" | ||||
|                                                 :class="'s-required ' + + completion_tag(cc)" | ||||
|                                                 ><i class='fa fa-asterisk' ></i | ||||
|                                             ></abbr> | ||||
|                                         </td> | ||||
|                                         <td><span :class="'r-completion-'+completion_tag(cc)" | ||||
|                                             ><i :class="'fa fa-'+completion_icon(cc)" :title="text['completion_'+completion_tag(cc)]"></i> | ||||
|                                             {{ (cc.proficient === null)?text.unrated:cc.grade }}</span></td> | ||||
|                                         <td><span class="text-info">{{ cc.points }} {{ text.points }}</span></td> | ||||
|                                         <td><span class="text-danger" v-if='cc.required'>{{ text.required }}</span></td> | ||||
|                                         <td> | ||||
|                                         </td> | ||||
|                                         <td v-if="cc.feedback"> | ||||
|                                             <a  v-b-modal="'r-competency-feedback-'+cc.id" | ||||
|                                                 href="#" | ||||
|  | @ -1782,10 +1795,10 @@ export default { | |||
|                         ungraded: 0, | ||||
|                     }; | ||||
| 
 | ||||
|                     if(this.value.course.completion){ | ||||
|                         for(const cond of this.value.course.completion.conditions){ | ||||
|                             for(const itm of cond.items){ | ||||
|                                 if(itm.progress){ | ||||
|                     if(this.value.course.completion) { | ||||
|                         for(const cond of this.value.course.completion.conditions) { | ||||
|                             for(const itm of cond.items) { | ||||
|                                 if(itm.progress) { | ||||
|                                     status.students += itm.progress.students; | ||||
|                                     status.completed += itm.progress.completed; | ||||
|                                     status.completed_pass += itm.progress.completed_pass; | ||||
|  | @ -1794,10 +1807,12 @@ export default { | |||
|                                 } | ||||
|                             } | ||||
|                         } | ||||
| 
 | ||||
|                     } else if (this.value.course.grades){ | ||||
|                         for( const g of this.value.course.grades){ | ||||
|                             if(g.grading){ | ||||
|                     } else if (this.value.course.competency) { | ||||
|                         status.students = this.value.course.competency.total; | ||||
|                         status.completed = this.value.course.competency.proficient; | ||||
|                     } else if (this.value.course.grades) { | ||||
|                         for( const g of this.value.course.grades) { | ||||
|                             if(g.grading) { | ||||
|                                 status.students += g.grading.students; | ||||
|                                 status.completed += g.grading.completed; | ||||
|                                 status.completed_pass += g.grading.completed_pass; | ||||
|  | @ -1805,7 +1820,7 @@ export default { | |||
|                                 status.ungraded += g.grading.ungraded; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                     }  | ||||
| 
 | ||||
|                     return status; | ||||
|                 }, | ||||
|  | @ -2276,7 +2291,7 @@ export default { | |||
|         }); | ||||
| 
 | ||||
| 
 | ||||
|         //TAG: Course competency
 | ||||
|         //TAG: Teacher Course competency
 | ||||
|         Vue.component('r-item-teacher-course-competency',{ | ||||
|             props: { | ||||
|                 value : { | ||||
|  | @ -2313,23 +2328,30 @@ export default { | |||
|                 }, | ||||
|             }, | ||||
|             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_icon(competency) { | ||||
|                     if (competency.proficient && competency.courseproficient) { | ||||
|                         return "check-circle"; | ||||
|                     } else if (competency.proficient) { | ||||
|                         return "check"; | ||||
|                     } else if (competency.proficient === false) { | ||||
|                         return "times-circle"; | ||||
|                     } else { | ||||
|                         return "circle-o"; | ||||
|                     } | ||||
|                 }, | ||||
| 
 | ||||
|                 completion_tag(cgroup){ | ||||
|                     return cgroup.completion?'completed':'incomplete'; | ||||
|                 completion_tag(competency){ | ||||
|                     if (competency.proficient && competency.courseproficient) { | ||||
|                         return "completed"; | ||||
|                     } else if (competency.proficient) { | ||||
|                         return "completed"; | ||||
|                     } else if (competency.proficient === false) { | ||||
|                         return "failed"; | ||||
|                     } else if (competency.progress) { | ||||
|                         return "progress"; | ||||
|                     } else { | ||||
|                         return "incomplete"; | ||||
|                     } | ||||
|                 }, | ||||
| 
 | ||||
|                 pathtags(competency) { | ||||
|  | @ -2342,48 +2364,55 @@ export default { | |||
|                         } | ||||
|                         let url; | ||||
|                         if (p.type =='competency') {  | ||||
|                             url = `/admin/tool/lp/competencies.php?competencyid=${p.id}`; | ||||
|                             url = `/admin/tool/lp/user_competency_in_course.php?courseid=${this.item.course.id}&competencyid=${p.id}`; | ||||
|                         } else { | ||||
|                             url = `/admin/tool/lp/competencies.php?competencyframeworkid=${p.id}&pagecontextid=${p.contextid}`; | ||||
|                             url = this.competencyurl(p); | ||||
|                         } | ||||
|                          | ||||
|                         s += `<a href="${url}">${p.title}</a>`; | ||||
|                         s += `<a href="${url}" target="_blank">${p.title}</a>`; | ||||
|                     } | ||||
|                     return s; | ||||
|                 }, | ||||
|                 requiredChanged(newValue,c){ | ||||
|                     call([{ | ||||
|                         methodname: 'local_treestudyplan_require_competency', | ||||
|                         args: { 'competency_id': c.id, | ||||
|                                 'item_id': this.item.id, | ||||
|                                 'required': newValue, | ||||
|                                 } | ||||
|                     }])[0].fail(notification.exception); | ||||
|                 }, | ||||
|                 competencyurl(c) { | ||||
|                     return `/admin/tool/lp/user_competency_in_course.php?courseid=${this.item.course.id}&competencyid=${c.id}`; | ||||
|                 } | ||||
|             }, | ||||
|             template: ` | ||||
|             <table class="t-item-course-competency-list"> | ||||
|             <table class="r-item-course-competency-list"> | ||||
|                 <tr v-if="value.competencies.length == 0"> | ||||
|                     <td colspan='2'>{{text.competencies_not_configured}}! | ||||
|                     <br><a :href="'/admin/tool/lp/coursecompetencies.php?courseid='+item.course.id" target='_blank'>{{text.configure_competencies}}</a> | ||||
|                     </td> | ||||
|                 </tr> | ||||
|                 <template v-else> | ||||
|                     <tr class='t-item-course-competency-headers'> | ||||
|                         <th>{{text.heading}}</th> | ||||
|                         <th></th> | ||||
|                         <th>{{text.required}}</th> | ||||
|                     </tr> | ||||
|                     <tr v-for='c in value.competencies'> | ||||
|                         <td :colspan="(c.details)?1:2"><a href='#' v-b-modal="'modal-competency-id-'+c.id"><span v-html='c.title'></span></a></td> | ||||
|                         <td class='details' v-if="c.details"> | ||||
|                             <a href='#' v-b-modal="'modal-competency-id-'+c.id"><span v-html='c.details'></span></a> | ||||
|                         </td> | ||||
|                         <td> | ||||
|                             <b-form-checkbox inline | ||||
|                                 @change="requiredChanged($event,c)" | ||||
|                                 v-model="c.required" | ||||
|                             >{{ text.required }}</b-form-checkbox> | ||||
|                             <a href="#" v-b-modal="'modal-competency-id-'+c.id"><span v-html='c.title'></span></a> | ||||
|                         </td> | ||||
|                         <td class='details'> | ||||
|                             <a  v-if="c.details" href="#" v-b-modal="'modal-competency-id-'+c.id"><span v-html='c.details'></span></a> | ||||
|                             <abbr v-if="c.required" :title="text.required" | ||||
|                                 :class="'s-required ' + + completion_tag(c)" | ||||
|                                 ><i class='fa fa-asterisk' ></i | ||||
|                             ></abbr>    | ||||
|                         </td> | ||||
|                         <td><r-completion-bar v-model="c.completionstats" :width="150" :height="15"></td> | ||||
|                         <td v-if="c.feedback"> | ||||
|                             <a  v-b-modal="'r-competency-feedback-'+c.id" | ||||
|                                 href="#" | ||||
|                             >{{ text["view_feedback"]}}</a> | ||||
|                             <b-modal | ||||
|                                 :id="'r-competency-feedback-'+c.id" | ||||
|                                 size="sm" | ||||
|                                 ok-only | ||||
|                                 centered | ||||
|                                 scrollable | ||||
|                             > | ||||
|                                 <template #modal-header> | ||||
|                                     <h2><i class="fa fa-puzzle-piece"></i>{{ c.title }}</h2><br> | ||||
|                                 </template> | ||||
|                                 <span v-html="c.feedback"></span> | ||||
|                             </b-modal> | ||||
|                         </td> | ||||
|                         <b-modal :id="'modal-competency-id-'+c.id" | ||||
|                             size="lg" | ||||
|  | @ -2404,17 +2433,41 @@ export default { | |||
| 
 | ||||
|                             <template v-if="c.rule && c.children"> | ||||
|                                 <div>{{ c.ruleoutcome }} {{ text.when}} <span v-html="c.rule.toLocaleLowerCase()"></span></div> | ||||
|                                 <table v-if="c.children" class='t-item-course-competency-list'> | ||||
|                                 <table v-if="c.children" class='r-item-course-competency-list'> | ||||
|                                     <tr class='t-item-course-competency-headers'> | ||||
|                                         <th>{{text.heading}}</th> | ||||
|                                         <th></th> | ||||
|                                         <th>{{text.required}}</th> | ||||
|                                         <th colspan="2">{{text.heading}}</th> | ||||
|                                         <th colspan="3">{{text.results}}</th> | ||||
|                                     </tr> | ||||
|                                     <tr v-for="cc in c.children"> | ||||
|                                         <td :colspan="(c.details)?1:2" ><span v-html='cc.title'></span></td> | ||||
|                                         <td class='details' v-if="cc.details"><span v-html='cc.details'></span></td> | ||||
|                                         <td> | ||||
|                                             <a :href='competencyurl(c)' target="_blank"><span v-html='cc.title'></span></a> | ||||
|                                         </td> | ||||
|                                         <td class='details'> | ||||
|                                             <a  v-if="cc.details" :href='competencyurl(c)' target="_blank"><span v-html='cc.details'></span></a> | ||||
|                                             <abbr v-if="c.required" :title="text.required" | ||||
|                                                 :class="'s-required ' + completion_tag(cc)" | ||||
|                                                 ><i class='fa fa-asterisk' ></i | ||||
|                                             ></abbr> | ||||
|                                         </td> | ||||
|                                         <td><r-completion-bar v-model="cc.completionstats" :width="150" :height="15"></r-completion-bar></td> | ||||
|                                         <td><span class="text-info">{{ cc.points }} {{ text.points }}</span></td> | ||||
|                                         <td><span class="text-danger" v-if='cc.required'>{{ text.required }}</span></td> | ||||
|                                         <td v-if="cc.feedback"> | ||||
|                                             <a  v-b-modal="'r-competency-feedback-'+cc.id" | ||||
|                                                 href="#" | ||||
|                                             >{{ text["view_feedback"]}}</a> | ||||
|                                             <b-modal | ||||
|                                                 :id="'r-competency-feedback-'+cc.id" | ||||
|                                                 size="sm" | ||||
|                                                 ok-only | ||||
|                                                 centered | ||||
|                                                 scrollable | ||||
|                                             > | ||||
|                                                 <template #modal-header> | ||||
|                                                     <h2><i class="fa fa-puzzle-piece"></i>{{ cc.title }}</h2><br> | ||||
|                                                 </template> | ||||
|                                                 <span v-html="cc.feedback"></span> | ||||
|                                             </b-modal> | ||||
|                                         </td> | ||||
|                                     </tr> | ||||
|                                 </table> | ||||
|                             </template> | ||||
|  | @ -2426,7 +2479,6 @@ export default { | |||
|         }); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|         Vue.component('r-grading-bar',{ | ||||
|             props: { | ||||
|                 value : { | ||||
|  |  | |||
|  | @ -16,6 +16,7 @@ import {objCopy,transportItem} from './studyplan-processor'; | |||
| import Debugger from './util/debugger'; | ||||
| import {download,upload} from './downloader'; | ||||
| import {ProcessStudyplan, ProcessStudyplanPage} from './studyplan-processor'; | ||||
| import {eventTypes as editSwEventTypes} from 'core/edit_switch'; | ||||
| 
 | ||||
| import TSComponents from './treestudyplan-components'; | ||||
| import mFormComponents from "./util/mform-helper"; | ||||
|  | @ -42,7 +43,6 @@ export default { | |||
|         Vue.use(TSComponents); | ||||
|         Vue.use(mFormComponents); | ||||
|         let debug = new Debugger("treestudyplan-editor"); | ||||
| 
 | ||||
|         /************************************ | ||||
|          *                                  * | ||||
|          * Treestudyplan Editor components  * | ||||
|  | @ -58,10 +58,15 @@ export default { | |||
|             return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length ); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         // Create new eventbus for interaction between item components
 | ||||
|         const ItemEventBus = new Vue(); | ||||
| 
 | ||||
|         // Add event listener for the edit mode event so we can react to it, or at the very least ignore it
 | ||||
|         document.addEventListener(editSwEventTypes.editModeSet,(e) => { | ||||
|             e.preventDefault(); | ||||
|             ItemEventBus.$emit('editModeSet', e.detail.editMode); | ||||
|         }); | ||||
| 
 | ||||
|         let string_keys = load_stringkeys({ | ||||
|             conditions: [ | ||||
|                 { value: 'ALL', textkey: 'condition_all'}, | ||||
|  | @ -249,10 +254,8 @@ export default { | |||
|                 dateexpire: "dateexpire", | ||||
|                 badgeinfo: "badgeinfo", | ||||
|             }, | ||||
| 
 | ||||
|         }); | ||||
| 
 | ||||
| 
 | ||||
|         /* | ||||
|         * T-STUDYPLAN-ADVANCED | ||||
|         */ | ||||
|  | @ -282,7 +285,6 @@ export default { | |||
|             mounted() { | ||||
|             }, | ||||
|             updated() { | ||||
| 
 | ||||
|             }, | ||||
|             computed: { | ||||
|                 scales(){ | ||||
|  | @ -1184,7 +1186,7 @@ export default { | |||
|         * T-STUDYPLAN | ||||
|         */ | ||||
|         Vue.component('t-studyplan', { | ||||
|             props: [ 'value', 'index', ], | ||||
|             props: [ 'value', 'index', 'initeditmode'], | ||||
|             data() { | ||||
|                 return { | ||||
|                     config: { | ||||
|  | @ -1257,13 +1259,16 @@ export default { | |||
|                 }; | ||||
|             }, | ||||
|             created() { | ||||
| 
 | ||||
|                     // Listener for the signal that a new connection was made and needs to be drawn
 | ||||
|                     // Sent by the incoming item  - By convention, outgoing items are responsible for drawing the lines
 | ||||
|                     ItemEventBus.$on('editModeSet', this.onMoodleEditModeChanged); | ||||
|             }, | ||||
|             mounted() { | ||||
|                 if(this.value.pages[0].studylines.length == 0){ | ||||
|                 /*if(this.value.pages[0].studylines.length == 0){ | ||||
|                     // start in editmode if studylines on first page are empty
 | ||||
|                     this.edit.studyline.editmode = true; | ||||
|                 } | ||||
|                 }*/ | ||||
|                 this.edit.studyline.editmode = this.initeditmode; | ||||
|                 this.$root.$emit('redrawLines'); | ||||
|             }, | ||||
|             updated() { | ||||
|  | @ -1276,6 +1281,9 @@ export default { | |||
|                 } | ||||
|             }, | ||||
|             methods: { | ||||
|                 onMoodleEditModeChanged(mode){ | ||||
|                     this.edit.studyline.editmode = mode; | ||||
|                 }, | ||||
|                 columns(page) { | ||||
|                     return 1+ (page.periods * 2); | ||||
|                 }, | ||||
|  | @ -1522,11 +1530,11 @@ export default { | |||
|                 selectedpageChanged(newTabIndex,prevTabIndex) { | ||||
|                     const page = this.value.pages[newTabIndex]; | ||||
| 
 | ||||
| /* | ||||
|                     if (page.studylines.length == 0) { | ||||
|                         this.edit.studyline.editmode = true; | ||||
|                     } else { | ||||
|                         this.edit.studyline.editmode = false; | ||||
|                     } | ||||
|                     }  | ||||
|                     */ | ||||
|                 } | ||||
|             } | ||||
|             , | ||||
|  |  | |||
|  | @ -186,6 +186,23 @@ abstract class aggregator { | |||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 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 false; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Determine if aggregation method makes use of item conditions | ||||
|      * @return bool True if aggregation method makes use of | ||||
|      */ | ||||
|     public function use_item_conditions(){ | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * Return the current configuration string. | ||||
|      * @return string Configuration string | ||||
|  | @ -201,6 +218,8 @@ abstract class aggregator { | |||
|      */ | ||||
|     public static function basic_structure($value = VALUE_REQUIRED) : \external_description { | ||||
|         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); | ||||
|     } | ||||
| 
 | ||||
|  | @ -210,6 +229,8 @@ abstract class aggregator { | |||
|      */ | ||||
|     public function basic_model() { | ||||
|         return [ | ||||
|             "useRequiredGrades" => $this->use_required_grades(), | ||||
|             "useItemConditions" => $this->use_item_conditions(), | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -32,6 +32,7 @@ use core_competency\competency; | |||
| use core_competency\api as c_api; | ||||
| use core_competency\competency_rule_points; | ||||
| use core_competency\evidence; | ||||
| use core_competency\user_competency; | ||||
| use stdClass; | ||||
| 
 | ||||
| /** | ||||
|  | @ -63,6 +64,29 @@ class coursecompetencyinfo { | |||
|         $this->modinfo = get_fast_modinfo($this->course); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Webservice structure for completion stats | ||||
|      * @param int $value Webservice requirement constant | ||||
|      */ | ||||
|     public static function completionstats_structure($value = VALUE_OPTIONAL) : \external_description { | ||||
|         return new \external_single_structure([ | ||||
|             "ungraded" => new \external_value(PARAM_INT, 'number of ungraded submissions'), | ||||
|             "completed" => new \external_value(PARAM_INT, 'number of completed students'), | ||||
|             "students" => new \external_value(PARAM_INT, 'number of students that should submit'), | ||||
|             "completed_pass" => new \external_value(PARAM_INT, 'number of completed-pass students'), | ||||
|             "completed_fail" => new \external_value(PARAM_INT, 'number of completed-fail students'), | ||||
|         ], "details about gradable submissions", $value); | ||||
|     } | ||||
| 
 | ||||
|     protected static function completionstats($stats){ | ||||
|         return [ | ||||
|             "students" => $stats->count, | ||||
|             "completed" =>  0, | ||||
|             "ungraded" => $stats->nneedreview, | ||||
|             "completed_pass" => $stats->nproficient, | ||||
|             "completed_fail" => $stats->nfailed, | ||||
|         ]; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Generic competency info structure for individual competency stats | ||||
|  | @ -85,8 +109,8 @@ class coursecompetencyinfo { | |||
|             "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), | ||||
|             "needreview" => new \external_value(PARAM_BOOL, 'waiting for review or review in progress',VALUE_OPTIONAL), | ||||
|             "completionstats" => static::completionstats_structure(VALUE_OPTIONAL), | ||||
|             "count" => new \external_value(PARAM_INT, 'number of students in stats',VALUE_OPTIONAL), | ||||
|             "required" => new \external_value(PARAM_BOOL, 'if required in parent competency rule',VALUE_OPTIONAL), | ||||
|             "points" => new \external_value(PARAM_INT, 'number of points in parent competency rule',VALUE_OPTIONAL), | ||||
|  | @ -107,8 +131,8 @@ class coursecompetencyinfo { | |||
|     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), | ||||
|            "proficient" => new \external_value(PARAM_INT, 'number of proficient user/competencys ',VALUE_OPTIONAL), | ||||
|            "total" => new \external_value(PARAM_INT, 'total number of gradable user/competencies',VALUE_OPTIONAL), | ||||
|         ], 'course completion info', $value); | ||||
|     } | ||||
| 
 | ||||
|  | @ -195,19 +219,19 @@ class coursecompetencyinfo { | |||
| 
 | ||||
|         $count = 0; | ||||
|         $nproficient = 0; | ||||
|         $ncourseproficient = 0; | ||||
| 
 | ||||
|         foreach($coursecompetencies as $c) { | ||||
|             $ci = $this->competencyinfo_model($c); | ||||
|             if(!empty($studentslist)){ | ||||
|             if(!empty($studentlist)){ | ||||
|                 $stats = $this->proficiency_stats($c,$studentlist); | ||||
|                 $count += $stats->count; | ||||
|                 $nproficient += $stats->nproficient; | ||||
|                 $ncourseproficient += $stats->ncourseproficient; | ||||
|                 // Copy proficiency stats to model.
 | ||||
|                 foreach ((array)$stats as $key => $value) { | ||||
|                     $ci[$key] = $value; | ||||
|                 } | ||||
|                 $ci['completionstats'] = self::completionstats($stats); | ||||
| 
 | ||||
|             } | ||||
|             $ci['required'] = $this->is_required($c); | ||||
| 
 | ||||
|  | @ -249,6 +273,10 @@ class coursecompetencyinfo { | |||
|                     foreach($dids as $did) { | ||||
|                         $cc = new competency($did); | ||||
|                         $cci = $this->competencyinfo_model($cc); | ||||
|                         if(!empty($studentlist)){ | ||||
|                             $stats = $this->proficiency_stats($cc,$studentlist); | ||||
|                             $cci['completionstats'] = self::completionstats($stats); | ||||
|                         } | ||||
|                         if($rule instanceof competency_rule_points) { | ||||
|                             if(array_key_exists($did,$crlist)) { | ||||
|                                 $cr = $crlist[$did]; | ||||
|  | @ -267,9 +295,9 @@ class coursecompetencyinfo { | |||
|         $info = [ | ||||
|             "competencies" => $cis, | ||||
|         ]; | ||||
|         if(!empty($studentslist)){ | ||||
|             $info["fproficient"] = (float)($nproficient)/(float)($count); | ||||
|             $info["fcourseproficient"] = (float)($ncourseproficient)/(float)($count); | ||||
|         if(!empty($studentlist)){ | ||||
|             $info["proficient"] = $nproficient; | ||||
|             $info["total"] = $count; | ||||
|         } | ||||
|         return $info; | ||||
|     } | ||||
|  | @ -413,12 +441,16 @@ class coursecompetencyinfo { | |||
|         $r->count = 0; | ||||
|         $r->nproficient = 0; | ||||
|         $r->ncourseproficient = 0; | ||||
|         $r->nneedreview = 0; | ||||
|         $r->nfailed = 0; | ||||
| 
 | ||||
|         foreach ($studentlist as $sid) { | ||||
|             $p = $this->proficiency($competency,$sid); | ||||
|             $r->count += 1; | ||||
|             $r->nproficient += ($p->proficient)?1:0; | ||||
|             $r->nproficient += ($p->proficient === true)?1:0; | ||||
|             $r->nfailed += ($p->proficient === false)?1:0; | ||||
|             $r->ncourseproficient += ($p->courseproficient)?1:0; | ||||
|             $r->nneedreview += ($p->needreview)?1:0; | ||||
|         } | ||||
|         return $r; | ||||
|     } | ||||
|  | @ -438,8 +470,11 @@ class coursecompetencyinfo { | |||
|         $r = new \stdClass(); | ||||
| 
 | ||||
|         $uc = c_api::get_user_competency($userid, $competencyid); | ||||
|         $r->proficient = $uc->get('proficiency'); | ||||
|         $proficiency = $uc->get('proficiency'); | ||||
|         $r->proficient = $proficiency; | ||||
|         $r->grade = $scale->get_nearest_item($uc->get('grade')); | ||||
|         $r->needreview = (!($r->proficient) && ($uc->get('status') > user_competency::STATUS_IDLE)); | ||||
|         $r->failed = $proficiency === false; | ||||
|         try { | ||||
|             // Only add course grade and proficiency if the competency is included in the course.
 | ||||
|             $ucc = c_api::get_user_competency_in_course($this->course->id,$userid,$competencyid); | ||||
|  | @ -449,7 +484,6 @@ class coursecompetencyinfo { | |||
|         return $r; | ||||
|     } | ||||
| 
 | ||||
|      | ||||
|     /** | ||||
|      * Retrieve course proficiency and overall proficiency for a competency and user | ||||
|      * | ||||
|  |  | |||
|  | @ -138,6 +138,14 @@ class bistate_aggregator extends \local_treestudyplan\aggregator { | |||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 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; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Aggregate completed/failed goals into one outcome | ||||
|      * @param int[] $completions List of completions (completion class constants) | ||||
|  |  | |||
|  | @ -82,6 +82,7 @@ if ($CFG->debugdeveloper) { | |||
| } | ||||
| $PAGE->requires->js_call_amd('local_treestudyplan/page-edit-plan', 'init', [$studyplancontext->id, $categoryid, [ | ||||
|     "defaultAggregation" => get_config("local_treestudyplan","aggregation_mode"), | ||||
|     "editMode" => $PAGE->user_is_editing() | ||||
| ]]); | ||||
| 
 | ||||
| $catlist = courseservice::list_accessible_categories_with_usage("edit"); | ||||
|  | @ -150,6 +151,7 @@ print $OUTPUT->header(); | |||
|                 v-model='activestudyplan' | ||||
|                 @moved="movedStudyplan" | ||||
|                 @toggletoolbox="toggletoolbox" | ||||
|                 :initeditmode="initialEditMode" | ||||
|             ></t-studyplan> | ||||
|             <div v-else-if='loadingstudyplan' class="spinner-border text-primary" role="status"> | ||||
|                 <span class="sr-only">Loading...</span> | ||||
|  |  | |||
|  | @ -1,77 +0,0 @@ | |||
| <?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/>.
 | ||||
| /** | ||||
|  * Entry point for other moodle modules to embed the user's report in a view | ||||
|  * @package    local_treestudyplan | ||||
|  * @copyright  2023 P.M. Kuipers | ||||
|  * @license    https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | ||||
|  */ | ||||
| 
 | ||||
| require_once("../../config.php"); | ||||
| 
 | ||||
| require_once($CFG->libdir.'/weblib.php'); | ||||
| 
 | ||||
| use local_treestudyplan; | ||||
| 
 | ||||
| $systemcontext = context_system::instance(); | ||||
| 
 | ||||
| $PAGE->set_url("/local/treestudyplan/myreport.php", array()); | ||||
| require_login(); | ||||
| 
 | ||||
| $PAGE->set_pagelayout('embedded'); | ||||
| $PAGE->set_context($systemcontext); | ||||
| $PAGE->set_title(get_string('report_invited', 'local_treestudyplan', "{$USER->firstname} {$USER->lastname}")); | ||||
| $PAGE->set_heading(get_string('report_invited', 'local_treestudyplan', "{$USER->firstname} {$USER->lastname}")); | ||||
| 
 | ||||
| // Load javascripts and specific css.
 | ||||
| $PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/bootstrap-vue/bootstrap-vue.css')); | ||||
| if ($CFG->debugdeveloper) { | ||||
|     $PAGE->requires->css(new moodle_url($CFG->wwwroot.'/local/treestudyplan/css/devstyles.css')); | ||||
| } | ||||
| $PAGE->requires->js_call_amd('local_treestudyplan/page-myreport', 'init'); | ||||
| 
 | ||||
| 
 | ||||
| /** | ||||
|  * Shortcut function to provide translations | ||||
|  * | ||||
|  * @param mixed $str Translation key | ||||
|  * @param null|string[] $param Parameters to pass to translation | ||||
|  * @param string $plugin Location to search for translation strings | ||||
|  * @return string Translation of key | ||||
|  */ | ||||
| function t($str, $param = null, $plugin = 'local_treestudyplan') { | ||||
|     print get_string($str, $plugin, $param); | ||||
| } | ||||
| 
 | ||||
| print $OUTPUT->header(); | ||||
| ?>
 | ||||
| <div class="m-buttonbar" style="margin-bottom: 1em; text-align: right;"> | ||||
| <a class="btn btn-primary" href="invitations.php" id="manage_invites"><i class="fa fa-share"></i> <?php t('manage_invites'); ?></a>
 | ||||
| </div> | ||||
| <div id='root'> | ||||
|     <div class='vue-loader' v-show='false'> | ||||
|         <div class="spinner-border text-primary" role="status"> | ||||
|             <span class="sr-only">Loading...</span> | ||||
|         </div> | ||||
|     </div> | ||||
|     <div v-cloak> | ||||
|         <r-report v-model="studyplans"></r-report> | ||||
|     </div> | ||||
| </div> | ||||
| 
 | ||||
| <?php | ||||
| 
 | ||||
| print $OUTPUT->footer(); | ||||
|  | @ -87,7 +87,7 @@ print "    <div v-cloak>"; | |||
| if ($am_teaching) { | ||||
|     print "        <r-report type='teaching' teachermode ></r-report>"; | ||||
| } else { | ||||
|     print "        <r-report type='own' ></r-report>"; | ||||
|     print "        <r-report type='own' :userid='userid'></r-report>"; | ||||
| 
 | ||||
| } | ||||
| print "    </div>"; | ||||
|  |  | |||
|  | @ -80,6 +80,8 @@ if ($CFG->debugdeveloper) { | |||
| } | ||||
| $PAGE->requires->js_call_amd('local_treestudyplan/page-view-plan', 'init', [$studyplancontext->id, $categoryid]); | ||||
| 
 | ||||
| // Disable edit switch.
 | ||||
| $PAGE->theme->haseditswitch = false; | ||||
| /** | ||||
|  * Shortcut function to provide translations | ||||
|  * | ||||
|  |  | |||
		Reference in a new issue
	
	 PMKuipers
						PMKuipers