diff --git a/amd/build/studyplan-report-components.min.js b/amd/build/studyplan-report-components.min.js
index 6f68b3a..897580c 100644
--- a/amd/build/studyplan-report-components.min.js
+++ b/amd/build/studyplan-report-components.min.js
@@ -1,3 +1,3 @@
-define("local_treestudyplan/studyplan-report-components",["exports","./util/string-helper","core/ajax","core/notification","./util/debugger","core/config","./treestudyplan-components"],(function(_exports,_stringHelper,_ajax,_notification,_debugger,_config,_treestudyplanComponents){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_notification=_interopRequireDefault(_notification),_debugger=_interopRequireDefault(_debugger),_config=_interopRequireDefault(_config),_treestudyplanComponents=_interopRequireDefault(_treestudyplanComponents);Math.PI;var _default={install(Vue){Vue.use(_treestudyplanComponents.default);let debug=new _debugger.default("treestudyplan-viewer"),strings=(0,_stringHelper.load_strings)({report:{loading:"loadinghelp@core",studyplan_past:"studyplan_past",studyplan_present:"studyplan_present",studyplan_future:"studyplan_future",back:"back"},invalid:{error:"error"},header:{overall:"overall",students:"students@core"},studentresults:{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",student_not_tracked:"student_not_tracked"}});Vue.component("q-studyplanreport",{props:{structure:{type:Object}},data:()=>({students:[],studentresults:{},expansioninfo:{periods:{},lines:{},items:{}},groupinfo:{},sorting:{name:"asc"}}),created(){this.loadStudents()},computed:{sortedstudents(){return this.students}},watch:{structure:{immediate:!0,handler(structure){for(const period of structure.periods){const pid=period.period.id;this.expansioninfo.periods[pid]||(this.$set(this.expansioninfo.periods,pid,{expanded:!1}),this.$set(this.expansioninfo.lines,period.period.id,{}));for(const line of period.lines){const lid=line.line.id;this.expansioninfo.lines[lid]||this.$set(this.expansioninfo.lines[pid],lid,{expanded:!0});for(const item of line.items)this.expansioninfo.items[item.id]||this.$set(this.expansioninfo.items,item.id,{expanded:!1})}}}}},methods:{loadStudents(){const self=this;(0,_ajax.call)([{methodname:"local_treestudyplan_all_associated_grouped",args:{studyplan_id:this.structure.studyplan.id}}])[0].then((function(response){self.students=response;for(const group of self.students){self.$set(self.groupinfo,group.label,{expand:!0});for(const student of group.users)self.$set(self.studentresults,student.id,{loading:!0,results:[]}),(0,_ajax.call)([{methodname:"local_treestudyplan_get_report_data",args:{pageid:self.structure.page.id,userid:student.id,firstperiod:self.structure.firstperiod,lastperiod:self.structure.lastperiod}}])[0].then((function(response){self.studentresults[student.id].loading=!1,self.studentresults[student.id].results=response})).catch(_notification.default.exception)}})).catch(_notification.default.exception)},expansionChanged(parm,id,val){parm="p"==parm[0]?"periods":"l"==parm[0]?"lines":"items",debug.info("Expansion Changed",parm,id,val),"lines"==parm?this.expansioninfo[parm][id[0]][id[1]].expanded=val:this.expansioninfo[parm][id].expanded=val}},template:"\n
\n
\n
\n \n \n \n \n \n \n
\n
\n "}),Vue.component("q-header",{props:{structure:{type:Object},sorting:{type:Object},expansion:{type:Object}},data:()=>({text:strings.header}),computed:{},methods:{conditions(item){const course=item.course,list=[];if(debug.info("Determining conditions",course),course.completion){debug.info("Has Competencies");for(const cmp of course.competencies)list.push({name:cmp.title})}else if(course.completion){debug.info("Has Core completion");for(const cnd of course.completion.conditions)for(const itm of cnd.items)list.push({name:itm.title})}else if(course.grades){debug.info("Has selected grades");for(const g of course.grades)list.push({name:g.name})}return list},colspanPeriod(period){if(this.expansion.periods[period.id].expanded){let sum=0;for(const l of period.lines)sum+=this.colspanLine(period,l);return sum}return 1},colspanLine(period,line){if(this.expansion.lines[period.id][line.id].expanded){let sum=0;for(const i of line.items)sum+=this.colspanItem(i);return sum}return 1},colspanItem(item){if(this.expansion.items[item.id].expanded){return 1+this.conditions(item).length}return 1}},mounted(){},updated(){},template:"\n \n "}),Vue.component("q-groupheading",{props:{structure:{type:Object}},data:()=>({}),computed:{},methods:{},template:"\n \n\n
\n "}),Vue.component("q-studentresults",{props:{student:{type:Object},structure:{type:Object},results:{type:Array},loading:{type:Boolean,default:!1},expansion:{type:Object}},data:()=>({text:strings.studentresults}),computed:{},methods:{useritems(line){const list=[];for(const item of line.items){let newitm=item;for(const itm of this.results)if(item.id==itm.id){newitm=itm;break}list.push(newitm)}return list}},template:'\n \n {{student.firstname}} {{student.lastname}} | \n \n \n \n \n \n <\n /td>\n \n <\n /td>\n \n \n \n | | \n \n \n | | \n \n
\n '}),Vue.component("q-courseresult",{props:{student:{type:Object},item:{type:Object},loading:{type:Boolean,default:!1}},data:()=>({text:strings.studentresults}),computed:{hasprogressinfo(){const course=this.item.course;return!!course.enrolled&&(course.completion||course.competency||course.grades)},completion_icon(){switch(this.item.completion){default:return"circle-o";case"pending":return"question-circle";case"failed":return"times-circle";case"progress":return"exclamation-circle";case"completed":case"good":case"excellent":return"check-circle"}}},methods:{},template:'\n \n
\n \n \n
\n \n \n
\n \n \n
\n '}),Vue.component("q-conditionresult",{props:{student:{type:Object},item:{type:Object},loading:{type:Boolean,default:!1},conditionidx:{type:Number}},data:()=>({text:strings.studentresults}),computed:{conditions(){const course=this.item.course;if(debug.info("Determining conditions",course),course.completion)return debug.info("Has Competencies"),course.competencies;if(course.completion){debug.info("Has Core completion");const list=[];for(const cnd of course.completion.conditions)for(const itm of cnd.items)list.push(itm);return list}return course.grades?course.grades:void 0},condition(){return this.conditionidx>=0&&this.conditionidx\n \n \n \n \n \n \n \n \n \n '})}};return _exports.default=_default,_exports.default}));
+define("local_treestudyplan/studyplan-report-components",["exports","./util/string-helper","core/ajax","core/notification","./util/debugger","core/config","./treestudyplan-components"],(function(_exports,_stringHelper,_ajax,_notification,_debugger,_config,_treestudyplanComponents){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_notification=_interopRequireDefault(_notification),_debugger=_interopRequireDefault(_debugger),_config=_interopRequireDefault(_config),_treestudyplanComponents=_interopRequireDefault(_treestudyplanComponents);const debug=new _debugger.default("treestudyplan-viewer");Math.PI;function conditions(item){const course=item.course,list=[];if(course.completion)for(const cmp of course.competencies)list.push(cmp);else if(course.completion)for(const cnd of course.completion.conditions)for(const itm of cnd.items)list.push(itm);else if(course.grades)for(const g of course.grades)g.selected&&list.push(g);return list}var _default={install(Vue){Vue.use(_treestudyplanComponents.default);let strings=(0,_stringHelper.load_strings)({report:{loading:"loadinghelp@core",studyplan_past:"studyplan_past",studyplan_present:"studyplan_present",studyplan_future:"studyplan_future",back:"back"},invalid:{error:"error"},header:{overall:"overall",students:"students@core"},studentresults:{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",student_not_tracked:"student_not_tracked"}});Vue.component("q-studyplanreport",{props:{structure:{type:Object}},data:()=>({students:[],studentresults:{},expansioninfo:{periods:{},lines:{},items:{}},groupinfo:{},sorting:{name:"asc"}}),created(){this.loadStudents()},watch:{structure:{immediate:!0,handler(structure){for(const period of structure.periods){const pid=period.period.id;this.expansioninfo.periods[pid]||(this.$set(this.expansioninfo.periods,pid,{expanded:!1}),this.$set(this.expansioninfo.lines,period.period.id,{}));for(const line of period.lines){const lid=line.line.id;this.expansioninfo.lines[lid]||this.$set(this.expansioninfo.lines[pid],lid,{expanded:!0});for(const item of line.items)this.expansioninfo.items[item.id]||this.$set(this.expansioninfo.items,item.id,{expanded:!1})}}}}},computed:{sortedstudents(){return this.students},resultColCount(){let count=0;for(const period of this.structure.periods){const pid=period.period.id;if(this.expansioninfo.periods[pid].expanded)for(const line of period.lines){const lid=line.line.id;if(this.expansioninfo.lines[pid][lid])for(const item of line.items)this.expansioninfo.items[item.id]?count+=1+conditions(item).length:count+=1;else count+=1}else count+=1}return count}},methods:{loadStudents(){const self=this;(0,_ajax.call)([{methodname:"local_treestudyplan_all_associated_grouped",args:{studyplan_id:this.structure.studyplan.id}}])[0].then((function(response){self.students=response;for(const group of self.students){self.$set(self.groupinfo,group.label,{expand:!0});for(const student of group.users)self.$set(self.studentresults,student.id,{loading:!0,results:[]}),(0,_ajax.call)([{methodname:"local_treestudyplan_get_report_data",args:{pageid:self.structure.page.id,userid:student.id,firstperiod:self.structure.firstperiod,lastperiod:self.structure.lastperiod}}])[0].then((function(response){self.studentresults[student.id].loading=!1,self.studentresults[student.id].results=response})).catch(_notification.default.exception)}})).catch(_notification.default.exception)},expansionChanged(parm,id,val){parm="p"==parm[0]?"periods":"l"==parm[0]?"lines":"items",debug.info("Expansion Changed",parm,id,val),"lines"==parm?this.expansioninfo[parm][id[0]][id[1]].expanded=val:this.expansioninfo[parm][id].expanded=val}},template:"\n \n "}),Vue.component("q-header",{props:{structure:{type:Object},sorting:{type:Object},expansion:{type:Object}},data:()=>({text:strings.header}),computed:{},methods:{conditions:item=>function(item){const course=item.course,list=[];if(course.completion)for(const cmp of course.competencies)list.push({name:cmp.title});else if(course.completion)for(const cnd of course.completion.conditions)for(const itm of cnd.items)list.push({name:itm.title});else if(course.grades)for(const g of course.grades)g.selected&&(debug.info("...included"),list.push({name:g.name}));return debug.info("Returning list",list),list}(item),colspanPeriod(period){const pid=period.period.id;if(debug.info("Checking period expansion",period,this.expansion.periods),this.expansion.periods[pid].expanded){let sum=0;for(const l of period.lines)sum+=this.colspanLine(period,l);return sum}return 1},colspanLine(period,line){const pid=period.period.id,lid=line.line.id;if(this.expansion.lines[pid][lid].expanded){let sum=0;for(const i of line.items)sum+=this.colspanItem(i);return sum}return 1},colspanItem(item){if(this.expansion.items[item.id].expanded){return 1+this.conditions(item).length}return 1}},mounted(){},updated(){},template:"\n \n "}),Vue.component("q-groupheading",{props:{structure:{type:Object}},data:()=>({}),computed:{},methods:{},template:"\n \n\n
\n "}),Vue.component("q-studentresults",{props:{student:{type:Object},structure:{type:Object},results:{type:Array},loading:{type:Boolean,default:!1},expansion:{type:Object}},data:()=>({text:strings.studentresults}),computed:{},methods:{useritems(line){const list=[];for(const item of line.items){let newitm=item;for(const itm of this.results)if(item.id==itm.id){newitm=itm;break}list.push(newitm)}return list},conditions:item=>conditions(item)},template:'\n \n {{student.firstname}} {{student.lastname}} | \n \n \n \n \n \n | \n \n | \n \n \n \n | \n \n \n | \n \n
\n '}),Vue.component("q-courseresult",{props:{student:{type:Object},item:{type:Object},loading:{type:Boolean,default:!1}},data:()=>({text:strings.studentresults}),computed:{hasprogressinfo(){const course=this.item.course;return!!course.enrolled&&(course.completion||course.competency||course.grades)},completion_icon(){switch(this.item.completion){default:return"circle-o";case"pending":return"question-circle";case"failed":return"times-circle";case"progress":return"exclamation-circle";case"completed":case"good":case"excellent":return"check-circle"}}},methods:{},template:'\n \n \n \n \n \n \n \n \n \n \n \n '}),Vue.component("q-conditionresult",{props:{student:{type:Object},item:{type:Object},loading:{type:Boolean,default:!1},conditionidx:{type:Number}},data:()=>({text:strings.studentresults}),computed:{conditions(){return conditions(this.item)},condition(){return this.conditionidx>=0&&this.conditionidx\n \n \n \n \n \n \n \n \n \n \n '})}};return _exports.default=_default,_exports.default}));
//# sourceMappingURL=studyplan-report-components.min.js.map
\ No newline at end of file
diff --git a/amd/build/studyplan-report-components.min.js.map b/amd/build/studyplan-report-components.min.js.map
index 512c7b1..73a52f3 100644
--- a/amd/build/studyplan-report-components.min.js.map
+++ b/amd/build/studyplan-report-components.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"studyplan-report-components.min.js","sources":["../src/studyplan-report-components.js"],"sourcesContent":["/*eslint no-var: \"error\"*/\n/*eslint no-console: \"off\"*/\n/*eslint no-unused-vars: warn */\n/*eslint max-len: [\"error\", { \"code\": 160 }] */\n/*eslint-disable no-trailing-spaces */\n/*eslint-env es6*/\n// Put this file in path/to/plugin/amd/src\n\nimport {load_strings} from './util/string-helper';\nimport {call} from 'core/ajax';\nimport notification from 'core/notification';\nimport Debugger from './util/debugger';\nimport Config from 'core/config';\nimport TSComponents from './treestudyplan-components';\n\n// Make π available as a constant\nconst π = Math.PI;\n// Gravity value for arrow lines - determines how much a line is pulled in the direction of the start/end before changing direction\nconst LINE_GRAVITY = 1.3;\n\n\nexport default {\n install(Vue/*,options*/){\n Vue.use(TSComponents);\n let debug = new Debugger(\"treestudyplan-viewer\");\n\n let strings = load_strings({\n report: {\n loading: \"loadinghelp@core\",\n studyplan_past: \"studyplan_past\",\n studyplan_present: \"studyplan_present\",\n studyplan_future: \"studyplan_future\",\n back: \"back\",\n },\n \n invalid: {\n error: 'error',\n },\n header: {\n overall: 'overall',\n students: 'students@core'\n },\n studentresults: {\n completion_incomplete: \"completion_incomplete\",\n completion_failed: \"completion_failed\",\n completion_pending: \"completion_pending\",\n completion_progress: \"completion_progress\",\n completion_completed: \"completion_completed\",\n completion_good: \"completion_good\",\n completion_excellent: \"completion_excellent\",\n student_not_tracked: \"student_not_tracked\",\n }\n });\n\n /************************************\n * *\n * Treestudyplan Viewer components *\n * *\n ************************************/\n\n Vue.component('q-studyplanreport', {\n props: {\n structure: {\n type: Object,\n },\n },\n data() {\n return {\n students: [],\n studentresults: {},\n expansioninfo: {\n periods: {},\n lines: {},\n items: {},\n },\n groupinfo: {},\n\n sorting: {\n name: \"asc\",\n }\n };\n },\n created() {\n this.loadStudents();\n },\n computed: {\n sortedstudents(){\n return this.students;\n }\n },\n watch:{\n structure: {\n immediate: true,\n handler (structure) {\n // (Re)build expansion info structure\n for (const period of structure.periods) {\n const pid = period.period.id;\n if (!this.expansioninfo.periods[pid]) {\n // Use this.$set to make sure the properties are reactive.\n this.$set(\n this.expansioninfo.periods, \n pid, \n {\n expanded: false,\n }\n );\n this.$set(\n this.expansioninfo.lines, \n period.period.id, \n {}\n );\n }\n for (const line of period.lines) {\n const lid = line.line.id;\n if (!this.expansioninfo.lines[lid]) {\n // Use this.$set to make sure the properties are reactive.\n this.$set(\n this.expansioninfo.lines[pid],\n lid, \n {\n expanded: true,\n }\n );\n }\n for (const item of line.items) {\n if (!this.expansioninfo.items[item.id]) {\n // Use this.$set to make sure the properties are reactive.\n this.$set(\n this.expansioninfo.items,\n item.id,\n {\n expanded: false,\n }\n );\n }\n }\n }\n }\n }\n }\n },\n methods: {\n loadStudents() {\n const self = this;\n call([{\n methodname: 'local_treestudyplan_all_associated_grouped',\n args: { studyplan_id: this.structure.studyplan.id}\n }])[0].then(function(response){\n self.students = response;\n for(const group of self.students) {\n self.$set(\n self.groupinfo,\n group.label,\n {\n expand: true,\n }\n );\n\n for(const student of group.users){\n self.$set(\n self.studentresults,\n student.id, \n {\n loading: true,\n results: [],\n }\n );\n call([{\n methodname: 'local_treestudyplan_get_report_data',\n args: { pageid: self.structure.page.id, \n userid: student.id,\n firstperiod: self.structure.firstperiod,\n lastperiod: self.structure.lastperiod,\n }\n }])[0].then(function(response){\n self.studentresults[student.id].loading = false;\n self.studentresults[student.id].results = response;\n }).catch(notification.exception);\n }\n }\n }).catch(notification.exception);\n },\n expansionChanged(parm, id, val) {\n if(parm[0] == 'p') {\n parm = 'periods';\n } else if(parm[0] == 'l') {\n parm = 'lines';\n } else {\n parm = 'items';\n }\n debug.info('Expansion Changed',parm,id,val);\n\n if (parm == 'lines') {\n this.expansioninfo[parm][id[0]][id[1]].expanded = val;\n } else {\n this.expansioninfo[parm][id].expanded = val;\n }\n\n }\n },\n template: `\n \n
\n
\n \n \n \n \n \n \n
\n
\n `,\n });\n\n Vue.component('q-header', {\n props: {\n structure: {\n type: Object,\n },\n sorting: {\n type: Object,\n },\n expansion: {\n type: Object\n },\n },\n data() {\n return {\n text: strings.header,\n };\n },\n computed: {\n },\n methods: {\n conditions(item) {\n const course = item.course;\n const list = [];\n debug.info(\"Determining conditions\", course);\n if (course.completion) {\n debug.info(\"Has Competencies\");\n for (const cmp of course.competencies) {\n list.push({\n name: cmp.title,\n });\n }\n } else if(course.completion) {\n debug.info(\"Has Core completion\");\n for (const cnd of course.completion.conditions) {\n for (const itm of cnd.items) {\n list.push({\n name: itm.title,\n });\n }\n }\n } else if(course.grades) {\n debug.info(\"Has selected grades\");\n for (const g of course.grades) {\n list.push({\n name: g.name,\n });\n }\n }\n\n return list;\n },\n colspanPeriod(period) {\n if (this.expansion.periods[period.id].expanded) {\n let sum = 0;\n for (const l of period.lines) {\n sum += this.colspanLine(period,l);\n }\n return sum;\n } else {\n return 1;\n }\n },\n colspanLine(period,line) {\n if (this.expansion.lines[period.id][line.id].expanded) {\n let sum = 0;\n for (const i of line.items) {\n sum += this.colspanItem(i);\n }\n return sum;\n } else {\n return 1;\n }\n },\n colspanItem(item) {\n if (this.expansion.items[item.id].expanded) {\n const cs = this.conditions(item);\n return 1+cs.length;\n } else {\n return 1;\n }\n }\n\n },\n mounted() {\n \n },\n updated() {\n },\n /* https://css-tricks.com/position-sticky-and-table-headers/ */\n /* TODO: Rework below to make use of tables. Use as main element. Then create multiple as needed for the headers.\n This should create a much better view than using divs overal.\n */\n template: `\n \n `,\n });\n\n Vue.component('q-groupheading', {\n props: {\n structure: {\n type: Object,\n },\n },\n data() {\n return {\n\n };\n },\n computed: {\n },\n methods: {\n },\n template: `\n \n\n
\n `,\n });\n\n Vue.component('q-studentresults', {\n props: {\n student: {\n type: Object,\n },\n structure: {\n type: Object,\n },\n results: {\n type: Array,\n },\n loading: {\n type: Boolean,\n default: false\n },\n expansion: {\n type: Object,\n },\n },\n data() {\n return {\n text: strings.studentresults,\n };\n },\n computed: {\n },\n methods: {\n useritems(line) {\n const list = [];\n for (const item of line.items) {\n let newitm = item;\n for (const itm of this.results) {\n if (item.id == itm.id) {\n newitm = itm;\n break;\n }\n }\n list.push(newitm);\n }\n return list;\n }\n },\n /* https://css-tricks.com/position-sticky-and-table-headers/ */\n /* TODO: Rework below to make use of tables. Use as main element. Then create multiple as needed for the headers.\n This should create a much better view than using divs overal.\n */\n template: `\n
\n {{student.firstname}} {{student.lastname}} | \n \n \n <\n /td>\n \n <\n /td>\n \n \n \n | | \n \n \n | \n \n
\n `,\n });\n\n Vue.component('q-courseresult', {\n props: {\n student: {\n type: Object,\n },\n item: {\n type: Object,\n },\n loading: {\n type: Boolean,\n default: false\n },\n },\n data() {\n return {\n text: strings.studentresults,\n };\n },\n computed: {\n hasprogressinfo() {\n const course = this.item.course;\n if (!course.enrolled) {\n return false;\n } else {\n return (course.completion || course.competency || course.grades);\n }\n },\n completion_icon() {\n const completion = this.item.completion;\n switch(completion){\n default: // case \"incomplete\"\n return \"circle-o\";\n case \"pending\":\n return \"question-circle\";\n case \"failed\":\n return \"times-circle\";\n case \"progress\":\n return \"exclamation-circle\";\n case \"completed\":\n return \"check-circle\";\n case \"good\":\n return \"check-circle\";\n case \"excellent\":\n return \"check-circle\";\n }\n },\n },\n methods: {\n },\n template: `\n \n
\n \n \n
\n \n \n
\n \n \n
\n `,\n });\n\n Vue.component('q-conditionresult', {\n props: {\n student: {\n type: Object,\n },\n item: {\n type: Object,\n },\n loading: {\n type: Boolean,\n default: false\n },\n conditionidx: {\n type: Number,\n }\n },\n data() {\n return {\n text: strings.studentresults,\n };\n },\n computed: {\n conditions() {\n const course = this.item.course;\n const list = [];\n debug.info(\"Determining conditions\", course);\n if (course.completion) {\n debug.info(\"Has Competencies\");\n return course.competencies;\n } else if(course.completion) {\n debug.info(\"Has Core completion\");\n const list = [];\n for (const cnd of course.completion.conditions) {\n for (const itm of cnd.items) {\n list.push(itm);\n }\n }\n return list;\n } else if(course.grades) {\n return course.grades;\n }\n },\n condition() {\n if (this.conditionidx >= 0 && this.conditionidx < this.conditions.length) {\n return this.conditions[this.conditionidx];\n } else {\n return null;\n }\n },\n hasprogressinfo() {\n const course = this.item.course;\n if (!course.enrolled) {\n return false;\n } else {\n return (course.completion || course.competency || course.grades);\n }\n },\n },\n methods: {\n completion_icon(completion) {\n switch(completion){\n default: // case \"incomplete\"\n return \"circle-o\";\n case \"pending\":\n return \"question-circle\";\n case \"failed\":\n return \"times-circle\";\n case \"progress\":\n return \"exclamation-circle\";\n case \"completed\":\n return \"check-circle\";\n case \"good\":\n return \"check-circle\";\n case \"excellent\":\n return \"check-circle\";\n }\n },\n },\n // TODO: Show actual grades when relevant at all (don;t forget the grade point completion requirement)\n template: `\n \n
\n \n \n
\n \n \n
\n \n
\n `,\n });\n\n\n\n },\n};"],"names":["Math","PI","install","Vue","use","TSComponents","debug","Debugger","strings","report","loading","studyplan_past","studyplan_present","studyplan_future","back","invalid","error","header","overall","students","studentresults","completion_incomplete","completion_failed","completion_pending","completion_progress","completion_completed","completion_good","completion_excellent","student_not_tracked","component","props","structure","type","Object","data","expansioninfo","periods","lines","items","groupinfo","sorting","name","created","loadStudents","computed","sortedstudents","this","watch","immediate","handler","period","pid","id","$set","expanded","line","lid","item","methods","self","methodname","args","studyplan_id","studyplan","then","response","group","label","expand","student","users","results","pageid","page","userid","firstperiod","lastperiod","catch","notification","exception","expansionChanged","parm","val","info","template","expansion","text","conditions","course","list","completion","cmp","competencies","push","title","cnd","itm","grades","g","colspanPeriod","sum","l","colspanLine","i","colspanItem","length","mounted","updated","Array","Boolean","default","useritems","newitm","hasprogressinfo","enrolled","competency","completion_icon","conditionidx","Number","condition"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"studyplan-report-components.min.js","sources":["../src/studyplan-report-components.js"],"sourcesContent":["/*eslint no-var: \"error\"*/\n/*eslint no-console: \"off\"*/\n/*eslint no-unused-vars: warn */\n/*eslint max-len: [\"error\", { \"code\": 160 }] */\n/*eslint-disable no-trailing-spaces */\n/*eslint-env es6*/\n// Put this file in path/to/plugin/amd/src\n\nimport {load_strings} from './util/string-helper';\nimport {call} from 'core/ajax';\nimport notification from 'core/notification';\nimport Debugger from './util/debugger';\nimport Config from 'core/config';\nimport TSComponents from './treestudyplan-components';\n\n\nconst debug = new Debugger(\"treestudyplan-viewer\");\n\n\n// Make π available as a constant\nconst π = Math.PI;\n// Gravity value for arrow lines - determines how much a line is pulled in the direction of the start/end before changing direction\nconst LINE_GRAVITY = 1.3;\n\n/**\n * Retrieve condition headers\n * @param {Object} item\n */\nfunction conditionHeaders(item) {\n const course = item.course;\n const list = [];\n if (course.completion) {\n for (const cmp of course.competencies) {\n list.push({\n name: cmp.title,\n });\n }\n } else if(course.completion) {\n for (const cnd of course.completion.conditions) {\n for (const itm of cnd.items) {\n list.push({\n name: itm.title,\n });\n }\n }\n } else if(course.grades) {\n for(const g of course.grades) {\n if (g.selected) {\n debug.info(\"...included\");\n list.push({\n name: g.name,\n });\n }\n }\n }\n debug.info(\"Returning list\",list);\n return list;\n}\n\n/**\n * Retrieve conditions\n * @param {Object} item\n */\nfunction conditions(item) {\n const course = item.course;\n const list = [];\n if (course.completion) {\n for (const cmp of course.competencies) {\n list.push(cmp);\n }\n } else if(course.completion) {\n for (const cnd of course.completion.conditions) {\n for (const itm of cnd.items) {\n list.push(itm);\n }\n }\n } else if(course.grades) {\n for(const g of course.grades) {\n if (g.selected) {\n list.push(g);\n }\n }\n }\n return list;\n}\n\n\nexport default {\n install(Vue/*,options*/){\n Vue.use(TSComponents);\n\n let strings = load_strings({\n report: {\n loading: \"loadinghelp@core\",\n studyplan_past: \"studyplan_past\",\n studyplan_present: \"studyplan_present\",\n studyplan_future: \"studyplan_future\",\n back: \"back\",\n },\n \n invalid: {\n error: 'error',\n },\n header: {\n overall: 'overall',\n students: 'students@core'\n },\n studentresults: {\n completion_incomplete: \"completion_incomplete\",\n completion_failed: \"completion_failed\",\n completion_pending: \"completion_pending\",\n completion_progress: \"completion_progress\",\n completion_completed: \"completion_completed\",\n completion_good: \"completion_good\",\n completion_excellent: \"completion_excellent\",\n student_not_tracked: \"student_not_tracked\",\n }\n });\n\n /************************************\n * *\n * Treestudyplan Viewer components *\n * *\n ************************************/\n\n Vue.component('q-studyplanreport', {\n props: {\n structure: {\n type: Object,\n },\n },\n data() {\n return {\n students: [],\n studentresults: {},\n expansioninfo: {\n periods: {},\n lines: {},\n items: {},\n },\n groupinfo: {},\n\n sorting: {\n name: \"asc\",\n }\n };\n },\n created() {\n this.loadStudents();\n },\n watch:{\n structure: {\n immediate: true,\n handler (structure) {\n // (Re)build expansion info structure\n for (const period of structure.periods) {\n const pid = period.period.id;\n if (!this.expansioninfo.periods[pid]) {\n // Use this.$set to make sure the properties are reactive.\n this.$set(\n this.expansioninfo.periods, \n pid, \n {\n expanded: false,\n }\n );\n this.$set(\n this.expansioninfo.lines, \n period.period.id, \n {}\n );\n }\n for (const line of period.lines) {\n const lid = line.line.id;\n if (!this.expansioninfo.lines[lid]) {\n // Use this.$set to make sure the properties are reactive.\n this.$set(\n this.expansioninfo.lines[pid],\n lid, \n {\n expanded: true,\n }\n );\n }\n for (const item of line.items) {\n if (!this.expansioninfo.items[item.id]) {\n // Use this.$set to make sure the properties are reactive.\n this.$set(\n this.expansioninfo.items,\n item.id,\n {\n expanded: false,\n }\n );\n }\n }\n }\n }\n }\n }\n },\n computed: {\n sortedstudents(){\n return this.students;\n },\n resultColCount(){\n let count = 0;\n for (const period of this.structure.periods) {\n const pid = period.period.id;\n if (!this.expansioninfo.periods[pid].expanded) {\n // This period is not expanded. Just one \n count += 1;\n } else {\n for (const line of period.lines) {\n const lid = line.line.id;\n if (!this.expansioninfo.lines[pid][lid]) {\n count +=1;\n } else { \n for (const item of line.items) {\n if (!this.expansioninfo.items[item.id]) {\n count += 1;\n } else {\n count += 1 + conditions(item).length;\n }\n }\n }\n }\n }\n }\n return count; \n }\n },\n methods: {\n loadStudents() {\n const self = this;\n call([{\n methodname: 'local_treestudyplan_all_associated_grouped',\n args: { studyplan_id: this.structure.studyplan.id}\n }])[0].then(function(response){\n self.students = response;\n for(const group of self.students) {\n self.$set(\n self.groupinfo,\n group.label,\n {\n expand: true,\n }\n );\n\n for(const student of group.users){\n self.$set(\n self.studentresults,\n student.id, \n {\n loading: true,\n results: [],\n }\n );\n call([{\n methodname: 'local_treestudyplan_get_report_data',\n args: { pageid: self.structure.page.id, \n userid: student.id,\n firstperiod: self.structure.firstperiod,\n lastperiod: self.structure.lastperiod,\n }\n }])[0].then(function(response){\n self.studentresults[student.id].loading = false;\n self.studentresults[student.id].results = response;\n }).catch(notification.exception);\n }\n }\n }).catch(notification.exception);\n },\n expansionChanged(parm, id, val) {\n if(parm[0] == 'p') {\n parm = 'periods';\n } else if(parm[0] == 'l') {\n parm = 'lines';\n } else {\n parm = 'items';\n }\n debug.info('Expansion Changed',parm,id,val);\n\n if (parm == 'lines') {\n this.expansioninfo[parm][id[0]][id[1]].expanded = val;\n } else {\n this.expansioninfo[parm][id].expanded = val;\n }\n },\n\n },\n template: `\n \n `,\n });\n\n Vue.component('q-header', {\n props: {\n structure: {\n type: Object,\n },\n sorting: {\n type: Object,\n },\n expansion: {\n type: Object\n },\n },\n data() {\n return {\n text: strings.header,\n };\n },\n computed: {\n },\n methods: {\n conditions(item) {\n return conditionHeaders(item);\n },\n colspanPeriod(period) {\n const pid = period.period.id;\n debug.info(\"Checking period expansion\",period,this.expansion.periods);\n if (this.expansion.periods[pid].expanded) {\n let sum = 0;\n for (const l of period.lines) {\n sum += this.colspanLine(period,l);\n }\n return sum;\n } else {\n return 1;\n }\n },\n colspanLine(period,line) {\n const pid = period.period.id;\n const lid = line.line.id;\n\n if (this.expansion.lines[pid][lid].expanded) {\n let sum = 0;\n for (const i of line.items) {\n sum += this.colspanItem(i);\n }\n return sum;\n } else {\n return 1;\n }\n },\n colspanItem(item) {\n if (this.expansion.items[item.id].expanded) {\n const cs = this.conditions(item);\n return 1+cs.length;\n } else {\n return 1;\n }\n }\n\n },\n mounted() {\n \n },\n updated() {\n },\n /* https://css-tricks.com/position-sticky-and-table-headers/ */\n /* TODO: Rework below to make use of tables. Use as main element. Then create multiple as needed for the headers.\n This should create a much better view than using divs overal.\n */\n template: `\n \n `,\n });\n\n Vue.component('q-groupheading', {\n props: {\n structure: {\n type: Object,\n },\n },\n data() {\n return {\n\n };\n },\n computed: {\n },\n methods: {\n },\n template: `\n
\n\n
\n `,\n });\n\n Vue.component('q-studentresults', {\n props: {\n student: {\n type: Object,\n },\n structure: {\n type: Object,\n },\n results: {\n type: Array,\n },\n loading: {\n type: Boolean,\n default: false\n },\n expansion: {\n type: Object,\n },\n },\n data() {\n return {\n text: strings.studentresults,\n };\n },\n computed: {\n },\n methods: {\n useritems(line) {\n const list = [];\n for (const item of line.items) {\n let newitm = item;\n for (const itm of this.results) {\n if (item.id == itm.id) {\n newitm = itm;\n break;\n }\n }\n list.push(newitm);\n }\n return list;\n },\n conditions(item) {\n return conditions(item);\n },\n },\n /* https://css-tricks.com/position-sticky-and-table-headers/ */\n /* TODO: Rework below to make use of tables. Use as main element. Then create multiple as needed for the headers.\n This should create a much better view than using divs overal.\n */\n template: `\n
\n {{student.firstname}} {{student.lastname}} | \n \n \n \n \n \n | \n \n | \n \n \n \n | \n \n \n | \n \n
\n `,\n });\n\n Vue.component('q-courseresult', {\n props: {\n student: {\n type: Object,\n },\n item: {\n type: Object,\n },\n loading: {\n type: Boolean,\n default: false\n },\n },\n data() {\n return {\n text: strings.studentresults,\n };\n },\n computed: {\n hasprogressinfo() {\n const course = this.item.course;\n if (!course.enrolled) {\n return false;\n } else {\n return (course.completion || course.competency || course.grades);\n }\n },\n completion_icon() {\n const completion = this.item.completion;\n switch(completion){\n default: // case \"incomplete\"\n return \"circle-o\";\n case \"pending\":\n return \"question-circle\";\n case \"failed\":\n return \"times-circle\";\n case \"progress\":\n return \"exclamation-circle\";\n case \"completed\":\n return \"check-circle\";\n case \"good\":\n return \"check-circle\";\n case \"excellent\":\n return \"check-circle\";\n }\n },\n },\n methods: {\n },\n template: `\n \n \n \n \n \n \n \n \n \n \n \n `,\n });\n\n Vue.component('q-conditionresult', {\n props: {\n student: {\n type: Object,\n },\n item: {\n type: Object,\n },\n loading: {\n type: Boolean,\n default: false\n },\n conditionidx: {\n type: Number,\n }\n },\n data() {\n return {\n text: strings.studentresults,\n };\n },\n computed: {\n conditions() {\n return conditions(this.item);\n },\n condition() {\n if (this.conditionidx >= 0 && this.conditionidx < this.conditions.length) {\n return this.conditions[this.conditionidx];\n } else {\n return null;\n }\n },\n hasprogressinfo() {\n const course = this.item.course;\n if (!course.enrolled) {\n return false;\n } else {\n return (course.completion || course.competency || course.grades);\n }\n },\n },\n methods: {\n completion_icon(completion) {\n switch(completion){\n default: // case \"incomplete\"\n return \"circle-o\";\n case \"pending\":\n return \"question-circle\";\n case \"failed\":\n return \"times-circle\";\n case \"progress\":\n return \"exclamation-circle\";\n case \"completed\":\n return \"check-circle\";\n case \"good\":\n return \"check-circle\";\n case \"excellent\":\n return \"check-circle\";\n }\n },\n },\n // TODO: Show actual grades when relevant at all (don;t forget the grade point completion requirement)\n template: `\n \n \n \n \n \n \n \n \n \n \n \n `,\n });\n\n\n\n },\n};"],"names":["debug","Debugger","Math","PI","conditions","item","course","list","completion","cmp","competencies","push","cnd","itm","items","grades","g","selected","install","Vue","use","TSComponents","strings","report","loading","studyplan_past","studyplan_present","studyplan_future","back","invalid","error","header","overall","students","studentresults","completion_incomplete","completion_failed","completion_pending","completion_progress","completion_completed","completion_good","completion_excellent","student_not_tracked","component","props","structure","type","Object","data","expansioninfo","periods","lines","groupinfo","sorting","name","created","loadStudents","watch","immediate","handler","period","pid","id","this","$set","expanded","line","lid","computed","sortedstudents","resultColCount","count","length","methods","self","methodname","args","studyplan_id","studyplan","then","response","group","label","expand","student","users","results","pageid","page","userid","firstperiod","lastperiod","catch","notification","exception","expansionChanged","parm","val","info","template","expansion","text","title","conditionHeaders","colspanPeriod","sum","l","colspanLine","i","colspanItem","mounted","updated","Array","Boolean","default","useritems","newitm","hasprogressinfo","enrolled","competency","completion_icon","conditionidx","Number","condition"],"mappings":"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"}
\ No newline at end of file
diff --git a/amd/src/studyplan-report-components.js b/amd/src/studyplan-report-components.js
index d7f4ab5..a9dac8a 100644
--- a/amd/src/studyplan-report-components.js
+++ b/amd/src/studyplan-report-components.js
@@ -13,16 +13,81 @@ import Debugger from './util/debugger';
import Config from 'core/config';
import TSComponents from './treestudyplan-components';
+
+const debug = new Debugger("treestudyplan-viewer");
+
+
// Make π available as a constant
const π = Math.PI;
// Gravity value for arrow lines - determines how much a line is pulled in the direction of the start/end before changing direction
const LINE_GRAVITY = 1.3;
+/**
+ * Retrieve condition headers
+ * @param {Object} item
+ */
+function conditionHeaders(item) {
+ const course = item.course;
+ const list = [];
+ if (course.completion) {
+ for (const cmp of course.competencies) {
+ list.push({
+ name: cmp.title,
+ });
+ }
+ } else if(course.completion) {
+ for (const cnd of course.completion.conditions) {
+ for (const itm of cnd.items) {
+ list.push({
+ name: itm.title,
+ });
+ }
+ }
+ } else if(course.grades) {
+ for(const g of course.grades) {
+ if (g.selected) {
+ debug.info("...included");
+ list.push({
+ name: g.name,
+ });
+ }
+ }
+ }
+ debug.info("Returning list",list);
+ return list;
+}
+
+/**
+ * Retrieve conditions
+ * @param {Object} item
+ */
+function conditions(item) {
+ const course = item.course;
+ const list = [];
+ if (course.completion) {
+ for (const cmp of course.competencies) {
+ list.push(cmp);
+ }
+ } else if(course.completion) {
+ for (const cnd of course.completion.conditions) {
+ for (const itm of cnd.items) {
+ list.push(itm);
+ }
+ }
+ } else if(course.grades) {
+ for(const g of course.grades) {
+ if (g.selected) {
+ list.push(g);
+ }
+ }
+ }
+ return list;
+}
+
export default {
install(Vue/*,options*/){
Vue.use(TSComponents);
- let debug = new Debugger("treestudyplan-viewer");
let strings = load_strings({
report: {
@@ -83,11 +148,6 @@ export default {
created() {
this.loadStudents();
},
- computed: {
- sortedstudents(){
- return this.students;
- }
- },
watch:{
structure: {
immediate: true,
@@ -139,6 +199,37 @@ export default {
}
}
},
+ computed: {
+ sortedstudents(){
+ return this.students;
+ },
+ resultColCount(){
+ let count = 0;
+ for (const period of this.structure.periods) {
+ const pid = period.period.id;
+ if (!this.expansioninfo.periods[pid].expanded) {
+ // This period is not expanded. Just one
+ count += 1;
+ } else {
+ for (const line of period.lines) {
+ const lid = line.line.id;
+ if (!this.expansioninfo.lines[pid][lid]) {
+ count +=1;
+ } else {
+ for (const item of line.items) {
+ if (!this.expansioninfo.items[item.id]) {
+ count += 1;
+ } else {
+ count += 1 + conditions(item).length;
+ }
+ }
+ }
+ }
+ }
+ }
+ return count;
+ }
+ },
methods: {
loadStudents() {
const self = this;
@@ -195,32 +286,30 @@ export default {
} else {
this.expansioninfo[parm][id].expanded = val;
}
+ },
- }
},
template: `
-
+
-
-
-
-
-
-
+
+
+
+
-
-
+
+
`,
});
@@ -245,38 +334,12 @@ export default {
},
methods: {
conditions(item) {
- const course = item.course;
- const list = [];
- debug.info("Determining conditions", course);
- if (course.completion) {
- debug.info("Has Competencies");
- for (const cmp of course.competencies) {
- list.push({
- name: cmp.title,
- });
- }
- } else if(course.completion) {
- debug.info("Has Core completion");
- for (const cnd of course.completion.conditions) {
- for (const itm of cnd.items) {
- list.push({
- name: itm.title,
- });
- }
- }
- } else if(course.grades) {
- debug.info("Has selected grades");
- for (const g of course.grades) {
- list.push({
- name: g.name,
- });
- }
- }
-
- return list;
+ return conditionHeaders(item);
},
colspanPeriod(period) {
- if (this.expansion.periods[period.id].expanded) {
+ const pid = period.period.id;
+ debug.info("Checking period expansion",period,this.expansion.periods);
+ if (this.expansion.periods[pid].expanded) {
let sum = 0;
for (const l of period.lines) {
sum += this.colspanLine(period,l);
@@ -287,7 +350,10 @@ export default {
}
},
colspanLine(period,line) {
- if (this.expansion.lines[period.id][line.id].expanded) {
+ const pid = period.period.id;
+ const lid = line.line.id;
+
+ if (this.expansion.lines[pid][lid].expanded) {
let sum = 0;
for (const i of line.items) {
sum += this.colspanItem(i);
@@ -319,39 +385,39 @@ export default {
template: `