Progress on front-end

This commit is contained in:
PMKuipers 2024-02-18 23:27:57 +01:00
parent 9f69b7bc15
commit 1f16c0b1ee
10 changed files with 816 additions and 98 deletions

View File

@ -1,3 +1,3 @@
define("local_treestudyplan/page-studyplan-report",["exports","core/ajax","core/notification","./vue/vue","./util/debugger","./util/string-helper","./studyplan-report-components","./modedit-modal","./portal-vue/portal-vue.esm","./bootstrap-vue/bootstrap-vue"],(function(_exports,_ajax,_notification,_vue,_debugger,_stringHelper,_studyplanReportComponents,_modeditModal,_portalVue,_bootstrapVue){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(studyplanid,pageid,firstperiod,lastperiod){if(void 0===pageid||!Number.isInteger(Number(pageid))||void 0===studyplanid||!Number.isInteger(Number(studyplanid)))return void debug.error("Did Error: studyplan id and page id not provided as integer numbers to script.",studyplanid,pageid,firstperiod,lastperiod);studyplanid=Number(studyplanid),pageid=Number(pageid);new _vue.default({el:"#root",data:{structure:null},async mounted(){},created(){this.loadStructure()},computed:{},methods:{loadStructure(){const self=this;this.structure=null,(0,_ajax.call)([{methodname:"local_treestudyplan_get_report_structure",args:{pageid:pageid}}])[0].then((function(response){self.structure=response})).catch(_notification.default.exception)}}})},_notification=_interopRequireDefault(_notification),_vue=_interopRequireDefault(_vue),_debugger=_interopRequireDefault(_debugger),_studyplanReportComponents=_interopRequireDefault(_studyplanReportComponents),_modeditModal=_interopRequireDefault(_modeditModal),_portalVue=_interopRequireDefault(_portalVue),_bootstrapVue=_interopRequireDefault(_bootstrapVue),_vue.default.use(_studyplanReportComponents.default),_vue.default.use(_modeditModal.default),_vue.default.use(_portalVue.default),_vue.default.use(_bootstrapVue.default);let debug=new _debugger.default("treestudyplanviewer");(0,_stringHelper.load_strings)({studyplan:{studyplan_select_placeholder:"studyplan_select_placeholder"}})})); define("local_treestudyplan/page-studyplan-report",["exports","core/ajax","core/notification","./vue/vue","./util/debugger","./util/string-helper","./studyplan-report-components","./report-viewer-components","./modedit-modal","./portal-vue/portal-vue.esm","./bootstrap-vue/bootstrap-vue"],(function(_exports,_ajax,_notification,_vue,_debugger,_stringHelper,_studyplanReportComponents,_reportViewerComponents,_modeditModal,_portalVue,_bootstrapVue){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=function(studyplanid,pageid,firstperiod,lastperiod){if(void 0===pageid||!Number.isInteger(Number(pageid))||void 0===studyplanid||!Number.isInteger(Number(studyplanid)))return void debug.error("Did Error: studyplan id and page id not provided as integer numbers to script.",studyplanid,pageid,firstperiod,lastperiod);studyplanid=Number(studyplanid),pageid=Number(pageid);new _vue.default({el:"#root",data:{structure:null},async mounted(){},created(){this.loadStructure()},computed:{},methods:{loadStructure(){const self=this;this.structure=null,(0,_ajax.call)([{methodname:"local_treestudyplan_get_report_structure",args:{pageid:pageid,firstperiod:firstperiod,lastperiod:lastperiod}}])[0].then((function(response){self.structure=response})).catch(_notification.default.exception)}}})},_notification=_interopRequireDefault(_notification),_vue=_interopRequireDefault(_vue),_debugger=_interopRequireDefault(_debugger),_studyplanReportComponents=_interopRequireDefault(_studyplanReportComponents),_reportViewerComponents=_interopRequireDefault(_reportViewerComponents),_modeditModal=_interopRequireDefault(_modeditModal),_portalVue=_interopRequireDefault(_portalVue),_bootstrapVue=_interopRequireDefault(_bootstrapVue),_vue.default.use(_studyplanReportComponents.default),_vue.default.use(_reportViewerComponents.default),_vue.default.use(_modeditModal.default),_vue.default.use(_portalVue.default),_vue.default.use(_bootstrapVue.default);let debug=new _debugger.default("treestudyplanviewer");(0,_stringHelper.load_strings)({studyplan:{studyplan_select_placeholder:"studyplan_select_placeholder"}})}));
//# sourceMappingURL=page-studyplan-report.min.js.map //# sourceMappingURL=page-studyplan-report.min.js.map

View File

@ -1 +1 @@
{"version":3,"file":"page-studyplan-report.min.js","sources":["../src/page-studyplan-report.js"],"sourcesContent":["/*eslint no-var: \"error\" */\n/*eslint no-unused-vars: \"off\" */\n/*eslint linebreak-style: \"off\" */\n/*eslint no-trailing-spaces: \"off\" */\n/*eslint-env es6*/\n\nimport {call} from 'core/ajax';\nimport notification from 'core/notification';\n\nimport Vue from './vue/vue';\n\nimport Debugger from './util/debugger';\nimport {load_strings} from './util/string-helper';\n\nimport SRComponents from './studyplan-report-components';\nVue.use(SRComponents);\nimport ModalComponents from './modedit-modal';\nVue.use(ModalComponents);\n\nimport PortalVue from './portal-vue/portal-vue.esm';\nVue.use(PortalVue);\nimport BootstrapVue from './bootstrap-vue/bootstrap-vue';\nVue.use(BootstrapVue);\n\n\nlet debug = new Debugger(\"treestudyplanviewer\");\n\nlet strings = load_strings({\n studyplan: {\n studyplan_select_placeholder: 'studyplan_select_placeholder',\n },\n});\n\n/**\n * Initialize the Page\n * @param {Number} studyplanid The id of the studyplan we need to view \n * @param {Number} pageid The id of the studyplan page we need to view \n * @param {Number} firstperiod The number of the first period to view\n * @param {Number} lastperiod The number of the last period to view \n */\nexport function init(studyplanid, pageid, firstperiod, lastperiod) {\n if (undefined === pageid || !Number.isInteger(Number(pageid)) ||\n undefined === studyplanid || !Number.isInteger(Number(studyplanid))) {\n debug.error(\"Did Error: studyplan id and page id not provided as integer numbers to script.\",\n studyplanid, pageid, firstperiod, lastperiod);\n return; // Do not continue if plan and page are not proper integers\n }\n // Ensure a numeric value instead of string.\n studyplanid = Number(studyplanid);\n pageid = Number(pageid);\n\n // Startup app.\n const app = new Vue({\n el: '#root',\n data: {\n structure: null,\n },\n async mounted() {\n \n },\n created() {\n this.loadStructure();\n },\n computed: {\n \n },\n methods: {\n loadStructure() {\n const self = this;\n this.structure = null; // Starts loading icon. Hides old data.\n call([{\n methodname: 'local_treestudyplan_get_report_structure',\n args: { pageid: pageid}\n }])[0].then(function(response){\n self.structure = response;\n }).catch(notification.exception);\n }\n },\n });\n}\n"],"names":["studyplanid","pageid","firstperiod","lastperiod","undefined","Number","isInteger","debug","error","Vue","el","data","structure","created","loadStructure","computed","methods","self","this","methodname","args","then","response","catch","notification","exception","use","SRComponents","ModalComponents","PortalVue","BootstrapVue","Debugger","studyplan","studyplan_select_placeholder"],"mappings":"4iBAwCqBA,YAAaC,OAAQC,YAAaC,oBAC/CC,IAAcH,SAAWI,OAAOC,UAAUD,OAAOJ,eACjDG,IAAcJ,cAAgBK,OAAOC,UAAUD,OAAOL,0BACtDO,MAAMC,MAAM,iFACRR,YAAaC,OAAQC,YAAaC,YAI1CH,YAAcK,OAAOL,aACrBC,OAASI,OAAOJ,QAGJ,IAAIQ,aAAI,CAChBC,GAAI,QACJC,KAAM,CACFC,UAAW,wBAKfC,eACSC,iBAETC,SAAU,GAGVC,QAAS,CACLF,sBACUG,KAAOC,UACRN,UAAY,oBACZ,CAAC,CACFO,WAAY,2CACZC,KAAM,CAAEnB,OAAQA,WAChB,GAAGoB,MAAK,SAASC,UACjBL,KAAKL,UAAYU,YAClBC,MAAMC,sBAAaC,mYA5DlCC,IAAIC,iDAEJD,IAAIE,oCAGJF,IAAIG,iCAEJH,IAAII,2BAGJvB,MAAQ,IAAIwB,kBAAS,wBAEX,8BAAa,CACvBC,UAAW,CACPC,6BAA8B"} {"version":3,"file":"page-studyplan-report.min.js","sources":["../src/page-studyplan-report.js"],"sourcesContent":["/*eslint no-var: \"error\" */\n/*eslint no-unused-vars: \"off\" */\n/*eslint linebreak-style: \"off\" */\n/*eslint no-trailing-spaces: \"off\" */\n/*eslint-env es6*/\n\nimport {call} from 'core/ajax';\nimport notification from 'core/notification';\n\nimport Vue from './vue/vue';\n\nimport Debugger from './util/debugger';\nimport {load_strings} from './util/string-helper';\n\nimport SRComponents from './studyplan-report-components';\nVue.use(SRComponents);\nimport RVComponents from './report-viewer-components';\nVue.use(RVComponents);\n\nimport ModalComponents from './modedit-modal';\nVue.use(ModalComponents);\n\nimport PortalVue from './portal-vue/portal-vue.esm';\nVue.use(PortalVue);\nimport BootstrapVue from './bootstrap-vue/bootstrap-vue';\nVue.use(BootstrapVue);\n\n\nlet debug = new Debugger(\"treestudyplanviewer\");\n\nlet strings = load_strings({\n studyplan: {\n studyplan_select_placeholder: 'studyplan_select_placeholder',\n },\n});\n\n/**\n * Initialize the Page\n * @param {Number} studyplanid The id of the studyplan we need to view \n * @param {Number} pageid The id of the studyplan page we need to view \n * @param {Number} firstperiod The number of the first period to view\n * @param {Number} lastperiod The number of the last period to view \n */\nexport function init(studyplanid, pageid, firstperiod, lastperiod) {\n if (undefined === pageid || !Number.isInteger(Number(pageid)) ||\n undefined === studyplanid || !Number.isInteger(Number(studyplanid))) {\n debug.error(\"Did Error: studyplan id and page id not provided as integer numbers to script.\",\n studyplanid, pageid, firstperiod, lastperiod);\n return; // Do not continue if plan and page are not proper integers\n }\n // Ensure a numeric value instead of string.\n studyplanid = Number(studyplanid);\n pageid = Number(pageid);\n\n // Startup app.\n const app = new Vue({\n el: '#root',\n data: {\n structure: null,\n },\n async mounted() {\n \n },\n created() {\n this.loadStructure();\n },\n computed: {\n \n },\n methods: {\n loadStructure() {\n const self = this;\n this.structure = null; // Starts loading icon. Hides old data.\n call([{\n methodname: 'local_treestudyplan_get_report_structure',\n args: { pageid: pageid,\n firstperiod: firstperiod,\n lastperiod: lastperiod\n }\n }])[0].then(function(response){\n self.structure = response;\n }).catch(notification.exception);\n }\n },\n });\n}\n"],"names":["studyplanid","pageid","firstperiod","lastperiod","undefined","Number","isInteger","debug","error","Vue","el","data","structure","created","loadStructure","computed","methods","self","this","methodname","args","then","response","catch","notification","exception","use","SRComponents","RVComponents","ModalComponents","PortalVue","BootstrapVue","Debugger","studyplan","studyplan_select_placeholder"],"mappings":"imBA2CqBA,YAAaC,OAAQC,YAAaC,oBAC/CC,IAAcH,SAAWI,OAAOC,UAAUD,OAAOJ,eACjDG,IAAcJ,cAAgBK,OAAOC,UAAUD,OAAOL,0BACtDO,MAAMC,MAAM,iFACRR,YAAaC,OAAQC,YAAaC,YAI1CH,YAAcK,OAAOL,aACrBC,OAASI,OAAOJ,QAGJ,IAAIQ,aAAI,CAChBC,GAAI,QACJC,KAAM,CACFC,UAAW,wBAKfC,eACSC,iBAETC,SAAU,GAGVC,QAAS,CACLF,sBACUG,KAAOC,UACRN,UAAY,oBACZ,CAAC,CACFO,WAAY,2CACZC,KAAM,CAAEnB,OAAQA,OACZC,YAAaA,YACbC,WAAYA,eAEhB,GAAGkB,MAAK,SAASC,UACjBL,KAAKL,UAAYU,YAClBC,MAAMC,sBAAaC,2cAlElCC,IAAIC,iDAEJD,IAAIE,8CAGJF,IAAIG,oCAGJH,IAAII,iCAEJJ,IAAIK,2BAGJxB,MAAQ,IAAIyB,kBAAS,wBAEX,8BAAa,CACvBC,UAAW,CACPC,6BAA8B"}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -14,6 +14,9 @@ import {load_strings} from './util/string-helper';
import SRComponents from './studyplan-report-components'; import SRComponents from './studyplan-report-components';
Vue.use(SRComponents); Vue.use(SRComponents);
import RVComponents from './report-viewer-components';
Vue.use(RVComponents);
import ModalComponents from './modedit-modal'; import ModalComponents from './modedit-modal';
Vue.use(ModalComponents); Vue.use(ModalComponents);
@ -70,7 +73,10 @@ export function init(studyplanid, pageid, firstperiod, lastperiod) {
this.structure = null; // Starts loading icon. Hides old data. this.structure = null; // Starts loading icon. Hides old data.
call([{ call([{
methodname: 'local_treestudyplan_get_report_structure', methodname: 'local_treestudyplan_get_report_structure',
args: { pageid: pageid} args: { pageid: pageid,
firstperiod: firstperiod,
lastperiod: lastperiod
}
}])[0].then(function(response){ }])[0].then(function(response){
self.structure = response; self.structure = response;
}).catch(notification.exception); }).catch(notification.exception);

View File

@ -38,32 +38,15 @@ export default {
studyplan_future: "studyplan_future", studyplan_future: "studyplan_future",
back: "back", back: "back",
}, },
invalid: { invalid: {
error: 'error', error: 'error',
}, },
grading: { header: {
ungraded: "ungraded", overall: 'overall',
graded: "graded", students: 'students@core'
allgraded: "allgraded",
unsubmitted: "unsubmitted",
nogrades: "nogrades",
unknown: "unknown",
}, },
completion: { studentresults: {
completed: "completion_completed",
incomplete: "completion_incomplete",
completed_pass: "completion_passed",
completed_fail: "completion_failed",
ungraded: "ungraded",
aggregation_all: "aggregation_all",
aggregation_any: "aggregation_any",
aggregation_one: "aggregation_one",
aggregation_overall_all: "aggregation_overall_all",
aggregation_overall_any: "aggregation_overall_any",
aggregation_overall_one: "aggregation_overall_one",
completion_not_configured: "completion_not_configured",
configure_completion: "configure_completion",
view_completion_report: "view_completion_report",
completion_incomplete: "completion_incomplete", completion_incomplete: "completion_incomplete",
completion_failed: "completion_failed", completion_failed: "completion_failed",
completion_pending: "completion_pending", completion_pending: "completion_pending",
@ -71,65 +54,8 @@ export default {
completion_completed: "completion_completed", completion_completed: "completion_completed",
completion_good: "completion_good", completion_good: "completion_good",
completion_excellent: "completion_excellent", completion_excellent: "completion_excellent",
view_feedback: "view_feedback",
coursetiming_past: "coursetiming_past",
coursetiming_present: "coursetiming_present",
coursetiming_future: "coursetiming_future",
required_goal: "required_goal",
student_not_tracked: "student_not_tracked", student_not_tracked: "student_not_tracked",
completion_not_enabled: "completion_not_enabled",
},
badge: {
share_badge: "share_badge",
dateissued: "dateissued",
dateexpire: "dateexpire",
badgeinfo: "badgeinfo",
badgeissuedstats: "badgeissuedstats",
completion_incomplete: "completion_incomplete_badge",
completion_completed: "completion_completed_badge",
completioninfo: "completioninfo",
badgedisabled: "badgedisabled"
},
course: {
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",
student_not_tracked: "student_not_tracked",
not_enrolled: "not_enrolled",
},
competency: {
competency_not_configured: "competency_not_configured",
configure_competency: "configure_competency",
when: "when",
required: "required",
points: "points@core_grades",
heading: "competency_heading",
details: "competency_details",
results: "results",
unrated: "unrated",
progress: "completion_progress",
view_feedback: "view_feedback",
},
pageinfo: {
edit: 'period_edit',
fullname: 'studyplan_name',
shortname: 'studyplan_shortname',
startdate: 'studyplan_startdate',
enddate: 'studyplan_enddate',
description: 'studyplan_description',
duration: 'studyplan_duration',
details: 'studyplan_details',
} }
}); });
/************************************ /************************************
@ -148,6 +74,12 @@ export default {
return { return {
students: [], students: [],
studentresults: {}, studentresults: {},
expansioninfo: {
periods: {},
lines: {},
items: {},
},
groupinfo: {},
sorting: { sorting: {
name: "asc", name: "asc",
@ -158,7 +90,60 @@ export default {
this.loadStudents(); this.loadStudents();
}, },
computed: { computed: {
sortedstudents(){
return this.students;
}
},
watch:{
structure: {
immediate: true,
handler (structure) {
// (Re)build expansion info structure
for (const period of structure.periods) {
const pid = period.period.id;
if (!this.expansioninfo.periods[pid]) {
// Use this.$set to make sure the properties are reactive.
this.$set(
this.expansioninfo.periods,
pid,
{
expanded: false,
}
);
this.$set(
this.expansioninfo.lines,
period.period.id,
{}
);
}
for (const line of period.lines) {
const lid = line.line.id;
if (!this.expansioninfo.lines[lid]) {
// Use this.$set to make sure the properties are reactive.
this.$set(
this.expansioninfo.lines[pid],
lid,
{
expanded: true,
}
);
}
for (const item of line.items) {
if (!this.expansioninfo.items[item.id]) {
// Use this.$set to make sure the properties are reactive.
this.$set(
this.expansioninfo.items,
item.id,
{
expanded: false,
}
);
}
}
}
}
}
}
}, },
methods: { methods: {
loadStudents() { loadStudents() {
@ -169,6 +154,14 @@ export default {
}])[0].then(function(response){ }])[0].then(function(response){
self.students = response; self.students = response;
for(const group of self.students) { for(const group of self.students) {
self.$set(
self.groupinfo,
group.label,
{
expand: true,
}
);
for(const student of group.users){ for(const student of group.users){
self.studentresults[student.id] = { self.studentresults[student.id] = {
loading: true, loading: true,
@ -188,20 +181,353 @@ export default {
} }
} }
}).catch(notification.exception); }).catch(notification.exception);
},
expansionChanged(parm, id, val) {
if(parm[0] == 'p') {
parm = 'periods';
} else if(parm[0] == 'l') {
parm = 'lines';
} else {
parm = 'items';
}
debug.info('Expansion Changed',parm,id,val);
if (parm == 'lines') {
this.expansioninfo[parm][id[0]][id[1]].expanded = val;
} else {
this.expansioninfo[parm][id].expanded = val;
}
} }
}, },
template: `
<div>
<q-header
:sorting='sorting'
:structure='structure'
:expansion='expansioninfo'
@expansion='expansionChanged'
></q-header>
<div class='q-scrolly'>
<template v-for="group in sortedstudents">
<q-groupheading v-if="group.users" :label="group.label" :groupinfo="groupinfo[group.label]"></q-groupheading>
<template v-if='group.users && groupinfo[group.label].expand'>
<q-studentresults v-for="student in group.users"
:student='student'
:structure='structure'
:results='studentresults[student.id]'
:expansion='expansioninfo'
></q-studentresults>
</template>
</template>
</div>
</div>
`,
});
Vue.component('q-header', {
props: {
structure: {
type: Object,
},
sorting: {
type: Object,
},
expansion: {
type: Object
},
},
data() {
return {
text: strings.header,
};
},
computed: {
},
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;
},
},
mounted() { mounted() {
}, },
updated() { updated() {
}, },
template: ` template: `
<div> <div class='q-header'>
<div class='q-studentname heading'><span>{{text.students}}</span></div>
<div v-for="p in structure.periods" class='q-period-heading' >
<div class='q-header-title'>
<a v-if="expansion.periods[p.period.id].expanded"
href='#' @click.prevent="$emit('expansion','periods',p.period.id,false);"><i class='fa fa-minus-square'></i></a>
<a v-else
href='#' @click.prevent="$emit('expansion','periods',p.period.id,true);"><i class='fa fa-plus-square'></i></a>
{{ p.period.fullname}}
</div>
<div v-if="expansion.periods[p.period.id].expanded" class='q-header-details'>
<div class='q-line-heading' v-for='l in p.lines'>
<div class='q-header-title'><span v-html="l.line.shortname"></span></div>
<div v-if="l.items.length == 1 || expansion.lines[p.period.id][l.line.id].expanded" class='q-header-details'>
<div class='q-item-heading' v-for='item in l.items'>
<div class='q-header-title'>
<a v-if="expansion.items[item.id].expanded"
href='#' @click.prevent="$emit('expansion','items',item.id,false);"
><i class='fa fa-minus-square'></i></a>
<a v-else
href='#' @click.prevent="$emit('expansion','items',item.id,true);"
><i class='fa fa-plus-square'></i></a>
{{ item.course.displayname}}
</div>
<div v-if="expansion.items[item.id].expanded" class='q-header-details'>
<div class='q-condition-heading'>
{{ text.overall }}
</div>
<div v-for="c in conditions(item)" class="q-condition-heading" >
<span v-html="c.name"></span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`,
});
Vue.component('q-groupheading', {
props: {
structure: {
type: Object,
},
},
data() {
return {
};
},
computed: {
},
methods: {
},
template: `
<div class='q-groupheading'>
</div> </div>
`, `,
}); });
Vue.component('q-studentresults', {
props: {
student: {
type: Object,
},
structure: {
type: Object,
},
results: {
type: Object
},
expansion: {
type: Object
},
},
data() {
return {
text: strings.studentresults,
};
},
computed: {
},
methods: {
hasprogressinfo(item) {
if (!item.course.enrolled) {
return false;
} else {
return (item.course.completion || item.course.competency || item.course.grades);
}
},
completion_icon(completion) {
switch(completion){
default: // case "incomplete"
return "circle-o";
case "pending":
return "question-circle";
case "failed":
return "times-circle";
case "progress":
return "exclamation-circle";
case "completed":
return "check-circle";
case "good":
return "check-circle";
case "excellent":
return "check-circle";
}
},
circle_icon(completion) {
switch(completion){
default: // case "incomplete"
return null;
case "failed":
return "times";
case "progress":
return "";
case "completed":
return "check";
case "good":
return "check";
case "excellent":
return "check";
}
},
courseprogress(item) {
if (!item.course.enrolled) {
return 0;
} else if(item.course.completion) {
return (item.course.completion.progress / item.course.completion.count);
} else if(item.course.competency) {
return (item.course.competency.progress / item.course.competency.count);
} else if(item.course.grades) {
return (this.gradeprogress(item.course.grades) / item.course.grades.length);
} else {
return 0;
}
},
gradeprogress(grades) {
let progress = 0;
for (const ix in grades) {
const g = grades[ix];
if (["completed","excellent","good"].includes(g.completion)) {
progress++;
}
}
return progress;
},
conditions(item) {
},
useritems(line) {
const list = [];
for (const item of line.items) {
let newitm = item;
for (const itm of this.results.results) {
if (item.id == itm.id) {
newitm = itm;
break;
}
}
list.push(newitm);
}
return list;
}
},
template: `
<div class='q-student-results'>
<div class='q-studentname '><span>{{student.firstname}} {{student.lastname}}</span></div>
<div v-for="p in structure.periods" class='q-period-results' >
<div v-if="expansion.periods[p.period.id].expanded" class='q-result-details'>
<div class='q-line-results' v-for='l in p.lines'>
<div v-if="l.items.length == 1 || expansion.lines[p.period.id][l.line.id].expanded" class='q-result-details'>
<div class='q-item-results' v-for='item in useritems(l)'>
<div v-if="expansion.items[item.id].expanded" class='q-result-details'>
<div class='q-result q-overviewresult'>
<template v-if='!item.course.enrolled'>
<i v-b-popover.top
class="r-course-result fa fa-exclamation-triangle t-not-enrolled-alert"
:title="text.student_not_tracked"></i>
</template>
<template v-else-if='hasprogressinfo(item)'>
<r-progress-circle v-if='["failed", "progress","incomplete"].includes(item.completion)'
:value='courseprogress(item)'
:max='1'
:min='0'
:class="'r-course-result r-completion-'+item.completion"
:icon='circle_icon(item.completion)'
:title="text['completion_'+item.completion]"
></r-progress-circle>
<i v-else v-b-popover.top
:class="'r-course-result fa fa-'+completion_icon(item.completion)+
' r-completion-'+item.completion"
:title="text['completion_'+item.completion]"></i>
</template>
<template v-else>
<i v-b-popover.top
:class="'r-course-result fa fa-'+completion_icon(item.completion)+
' r-completion-'+item.completion"
:title="text['completion_'+item.completion]"></i>
</template>
</div>
<div class='q-condition-heading' v-for='c in conditions(item)'>
</div>
</div>
<div v-else class='q-result q-overviewresult'>
<template v-if='!item.course.enrolled'>
<i v-b-popover.top
class="r-course-result fa fa-exclamation-triangle t-not-enrolled-alert"
:title="text.student_not_tracked"></i>
</template>
<template v-else-if='hasprogressinfo(item)'>
<r-progress-circle v-if='["failed", "progress","incomplete"].includes(item.completion)'
:value='courseprogress(item)'
:max='1'
:min='0'
:class="'r-course-result r-completion-'+item.completion"
:icon='circle_icon(item.completion)'
:title="text['completion_'+item.completion]"
></r-progress-circle>
<i v-else v-b-popover.top
:class="'r-course-result fa fa-'+completion_icon(item.completion)+
' r-completion-'+item.completion"
:title="text['completion_'+item.completion]"></i>
</template>
<template v-else>
<i v-b-popover.top
:class="'r-course-result fa fa-'+completion_icon(item.completion)+
' r-completion-'+item.completion"
:title="text['completion_'+item.completion]"></i>
</template>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`,
});
}, },
}; };

View File

@ -1575,9 +1575,157 @@ body.path-local-treestudyplan .editmode-switch-form > * {
color: var(--danger); color: var(--danger);
} }
.path-local-treestudyplan-studyplan-report { .path-local-treestudyplan {
font: inherit; font: inherit;
} }
.path-local-treestudyplan .q-header,
.path-local-treestudyplan .q-student-results {
background-color: white;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
}
.path-local-treestudyplan .q-header .q-period-heading,
.path-local-treestudyplan .q-header .q-line-heading,
.path-local-treestudyplan .q-header .q-item-heading,
.path-local-treestudyplan .q-header .q-condition-heading,
.path-local-treestudyplan .q-header .q-period-results,
.path-local-treestudyplan .q-header .q-line-results,
.path-local-treestudyplan .q-header .q-item-results,
.path-local-treestudyplan .q-header .q-condition-results,
.path-local-treestudyplan .q-student-results .q-period-heading,
.path-local-treestudyplan .q-student-results .q-line-heading,
.path-local-treestudyplan .q-student-results .q-item-heading,
.path-local-treestudyplan .q-student-results .q-condition-heading,
.path-local-treestudyplan .q-student-results .q-period-results,
.path-local-treestudyplan .q-student-results .q-line-results,
.path-local-treestudyplan .q-student-results .q-item-results,
.path-local-treestudyplan .q-student-results .q-condition-results {
display: flex;
flex-direction: column;
flex-grow: 1;
}
.path-local-treestudyplan .q-header .q-period-heading:not(:last-child),
.path-local-treestudyplan .q-header .q-line-heading:not(:last-child),
.path-local-treestudyplan .q-header .q-item-heading:not(:last-child),
.path-local-treestudyplan .q-header .q-condition-heading:not(:last-child),
.path-local-treestudyplan .q-header .q-period-results:not(:last-child),
.path-local-treestudyplan .q-header .q-line-results:not(:last-child),
.path-local-treestudyplan .q-header .q-item-results:not(:last-child),
.path-local-treestudyplan .q-header .q-condition-results:not(:last-child),
.path-local-treestudyplan .q-student-results .q-period-heading:not(:last-child),
.path-local-treestudyplan .q-student-results .q-line-heading:not(:last-child),
.path-local-treestudyplan .q-student-results .q-item-heading:not(:last-child),
.path-local-treestudyplan .q-student-results .q-condition-heading:not(:last-child),
.path-local-treestudyplan .q-student-results .q-period-results:not(:last-child),
.path-local-treestudyplan .q-student-results .q-line-results:not(:last-child),
.path-local-treestudyplan .q-student-results .q-item-results:not(:last-child),
.path-local-treestudyplan .q-student-results .q-condition-results:not(:last-child) {
border-right: 1px solid gray;
}
.path-local-treestudyplan .q-header .q-period-heading .q-header-title,
.path-local-treestudyplan .q-header .q-line-heading .q-header-title,
.path-local-treestudyplan .q-header .q-item-heading .q-header-title,
.path-local-treestudyplan .q-header .q-condition-heading .q-header-title,
.path-local-treestudyplan .q-header .q-period-results .q-header-title,
.path-local-treestudyplan .q-header .q-line-results .q-header-title,
.path-local-treestudyplan .q-header .q-item-results .q-header-title,
.path-local-treestudyplan .q-header .q-condition-results .q-header-title,
.path-local-treestudyplan .q-student-results .q-period-heading .q-header-title,
.path-local-treestudyplan .q-student-results .q-line-heading .q-header-title,
.path-local-treestudyplan .q-student-results .q-item-heading .q-header-title,
.path-local-treestudyplan .q-student-results .q-condition-heading .q-header-title,
.path-local-treestudyplan .q-student-results .q-period-results .q-header-title,
.path-local-treestudyplan .q-student-results .q-line-results .q-header-title,
.path-local-treestudyplan .q-student-results .q-item-results .q-header-title,
.path-local-treestudyplan .q-student-results .q-condition-results .q-header-title {
padding: 0.5rem;
}
.path-local-treestudyplan .q-header .q-period-heading .q-header-details,
.path-local-treestudyplan .q-header .q-period-heading .q-result-details,
.path-local-treestudyplan .q-header .q-line-heading .q-header-details,
.path-local-treestudyplan .q-header .q-line-heading .q-result-details,
.path-local-treestudyplan .q-header .q-item-heading .q-header-details,
.path-local-treestudyplan .q-header .q-item-heading .q-result-details,
.path-local-treestudyplan .q-header .q-condition-heading .q-header-details,
.path-local-treestudyplan .q-header .q-condition-heading .q-result-details,
.path-local-treestudyplan .q-header .q-period-results .q-header-details,
.path-local-treestudyplan .q-header .q-period-results .q-result-details,
.path-local-treestudyplan .q-header .q-line-results .q-header-details,
.path-local-treestudyplan .q-header .q-line-results .q-result-details,
.path-local-treestudyplan .q-header .q-item-results .q-header-details,
.path-local-treestudyplan .q-header .q-item-results .q-result-details,
.path-local-treestudyplan .q-header .q-condition-results .q-header-details,
.path-local-treestudyplan .q-header .q-condition-results .q-result-details,
.path-local-treestudyplan .q-student-results .q-period-heading .q-header-details,
.path-local-treestudyplan .q-student-results .q-period-heading .q-result-details,
.path-local-treestudyplan .q-student-results .q-line-heading .q-header-details,
.path-local-treestudyplan .q-student-results .q-line-heading .q-result-details,
.path-local-treestudyplan .q-student-results .q-item-heading .q-header-details,
.path-local-treestudyplan .q-student-results .q-item-heading .q-result-details,
.path-local-treestudyplan .q-student-results .q-condition-heading .q-header-details,
.path-local-treestudyplan .q-student-results .q-condition-heading .q-result-details,
.path-local-treestudyplan .q-student-results .q-period-results .q-header-details,
.path-local-treestudyplan .q-student-results .q-period-results .q-result-details,
.path-local-treestudyplan .q-student-results .q-line-results .q-header-details,
.path-local-treestudyplan .q-student-results .q-line-results .q-result-details,
.path-local-treestudyplan .q-student-results .q-item-results .q-header-details,
.path-local-treestudyplan .q-student-results .q-item-results .q-result-details,
.path-local-treestudyplan .q-student-results .q-condition-results .q-header-details,
.path-local-treestudyplan .q-student-results .q-condition-results .q-result-details {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
border-top: 1px solid green;
}
.path-local-treestudyplan .q-header .q-result,
.path-local-treestudyplan .q-student-results .q-result {
height: 2rem;
}
.path-local-treestudyplan .q-header,
.path-local-treestudyplan .q-student-results {
/*.q-line-heading > .q-header-title,*/
}
.path-local-treestudyplan .q-header .q-item-heading > .q-header-title,
.path-local-treestudyplan .q-header .q-condition-heading,
.path-local-treestudyplan .q-student-results .q-item-heading > .q-header-title,
.path-local-treestudyplan .q-student-results .q-condition-heading {
width: 2rem;
writing-mode: vertical-lr;
text-orientation: sideways;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.path-local-treestudyplan .q-header .q-line-heading > .q-header-title,
.path-local-treestudyplan .q-student-results .q-line-heading > .q-header-title {
height: fit-content;
}
.path-local-treestudyplan .q-header .q-item-heading > .q-item-heading,
.path-local-treestudyplan .q-student-results .q-item-heading > .q-item-heading {
height: 6rem;
}
.path-local-treestudyplan .q-header .q-condition-heading,
.path-local-treestudyplan .q-student-results .q-condition-heading {
height: 8rem;
}
.path-local-treestudyplan .q-studentname {
padding: 0.5em;
border-right: 2px solid grey;
width: 20rem;
}
.path-local-treestudyplan .q-studentname.heading {
font-weight: bold;
vertical-align: bottom;
display: flex;
}
.path-local-treestudyplan .q-studentname.heading span {
align-self: flex-end;
}
.path-local-treestudyplan .q-scrolly {
overflow-y: auto;
max-height: 300rem;
}
.path-local-treestudyplan .b-modal-justify-footer-between .modal-footer, .path-local-treestudyplan .b-modal-justify-footer-between .modal-footer,
.features-treestudyplan .b-modal-justify-footer-between .modal-footer { .features-treestudyplan .b-modal-justify-footer-between .modal-footer {

View File

@ -1,5 +1,91 @@
.path-local-treestudyplan-studyplan-report { .path-local-treestudyplan {
font: inherit; font: inherit;
.q-header, .q-student-results {
background-color: white;
display: flex;
flex-direction: row;;
flex-wrap: nowrap;
.q-period-heading, .q-line-heading, .q-item-heading, .q-condition-heading,
.q-period-results, .q-line-results, .q-item-results, .q-condition-results,
{
display: flex;
flex-direction: column;
flex-grow: 1;
&:not(:last-child) {
border-right: 1px solid gray;
}
.q-header-title {
padding: 0.5rem;
}
.q-header-details,
.q-result-details {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
border-top: 1px solid green;
}
}
.q-result {
height: 2rem;
}
}
.q-header, .q-student-results {
/*.q-line-heading > .q-header-title,*/
.q-item-heading > .q-header-title,
.q-condition-heading {
width: 2rem;
writing-mode: vertical-lr;
text-orientation: sideways;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.q-line-heading > .q-header-title {
height: fit-content;
}
.q-item-heading > .q-item-heading {
height: 6rem;
}
.q-condition-heading {
height: 8rem;
}
}
.q-studentname {
padding: 0.5em;
&.heading {
font-weight: bold;
vertical-align: bottom;
display: flex;
span {
align-self:flex-end;
}
}
border-right: 2px solid grey;
width: 20rem;
}
.q-scrolly {
overflow-y: auto;
max-height: 300rem;
}
} }

View File

@ -38,17 +38,17 @@ $pageid = required_param('page', PARAM_INT); // Category id.
$page = studyplanpage::find_by_id($pageid); $page = studyplanpage::find_by_id($pageid);
$studyplan = $page->studyplan(); $studyplan = $page->studyplan();
$context = $studyplan->context(); $context = $studyplan->context();
$contexname = (new contextinfo($context))->pathstr(); $ci = new contextinfo($context);
$contextname = $ci->pathstr();
$firstperiod = optional_param('firstperiod', 0, PARAM_INT); // First period to show $firstperiod = optional_param('firstperiod', 0, PARAM_INT); // First period to show
$lastperiod = optional_param('lastperiod', 0, PARAM_INT); // Last periode to show $lastperiod = optional_param('lastperiod', 0, PARAM_INT); // Last periode to show
$PAGE->set_pagelayout('base'); $PAGE->set_pagelayout('report');
//$PAGE->set_context($studyplancontext); $PAGE->set_title(get_string('studyplan_report', 'local_treestudyplan'));
$PAGE->set_title(get_string('studyplan_report', 'local_treestudyplan')." - ".$contextname); $PAGE->set_heading(get_string('studyplan_report', 'local_treestudyplan'));
$PAGE->set_heading(get_string('studyplan_report', 'local_treestudyplan')." - ".$contextname);
if (!premium::enabled()) { if (!premium::enabled()) {
throw new \moodle_exception("error:nopremiumaccess","local_treestudyplan","",premium::statusdescription()); throw new \moodle_exception("error:nopremiumaccess","local_treestudyplan","",premium::statusdescription());
@ -79,6 +79,10 @@ function t($str, $param = null, $plugin = 'local_treestudyplan') {
} }
print $OUTPUT->header(); print $OUTPUT->header();
print "<h3><b>{$contextname}</b> / {$studyplan->name()}</h3>";
if($studyplan->name() != $page->fullname()) {
print "<h4>{$page->fullname()}</h4>";
}
?> ?>
<div id='root'> <div id='root'>
<div class='vue-loader' v-show='false'> <div class='vue-loader' v-show='false'>

View File

@ -1575,9 +1575,157 @@ body.path-local-treestudyplan .editmode-switch-form > * {
color: var(--danger); color: var(--danger);
} }
.path-local-treestudyplan-studyplan-report { .path-local-treestudyplan {
font: inherit; font: inherit;
} }
.path-local-treestudyplan .q-header,
.path-local-treestudyplan .q-student-results {
background-color: white;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
}
.path-local-treestudyplan .q-header .q-period-heading,
.path-local-treestudyplan .q-header .q-line-heading,
.path-local-treestudyplan .q-header .q-item-heading,
.path-local-treestudyplan .q-header .q-condition-heading,
.path-local-treestudyplan .q-header .q-period-results,
.path-local-treestudyplan .q-header .q-line-results,
.path-local-treestudyplan .q-header .q-item-results,
.path-local-treestudyplan .q-header .q-condition-results,
.path-local-treestudyplan .q-student-results .q-period-heading,
.path-local-treestudyplan .q-student-results .q-line-heading,
.path-local-treestudyplan .q-student-results .q-item-heading,
.path-local-treestudyplan .q-student-results .q-condition-heading,
.path-local-treestudyplan .q-student-results .q-period-results,
.path-local-treestudyplan .q-student-results .q-line-results,
.path-local-treestudyplan .q-student-results .q-item-results,
.path-local-treestudyplan .q-student-results .q-condition-results {
display: flex;
flex-direction: column;
flex-grow: 1;
}
.path-local-treestudyplan .q-header .q-period-heading:not(:last-child),
.path-local-treestudyplan .q-header .q-line-heading:not(:last-child),
.path-local-treestudyplan .q-header .q-item-heading:not(:last-child),
.path-local-treestudyplan .q-header .q-condition-heading:not(:last-child),
.path-local-treestudyplan .q-header .q-period-results:not(:last-child),
.path-local-treestudyplan .q-header .q-line-results:not(:last-child),
.path-local-treestudyplan .q-header .q-item-results:not(:last-child),
.path-local-treestudyplan .q-header .q-condition-results:not(:last-child),
.path-local-treestudyplan .q-student-results .q-period-heading:not(:last-child),
.path-local-treestudyplan .q-student-results .q-line-heading:not(:last-child),
.path-local-treestudyplan .q-student-results .q-item-heading:not(:last-child),
.path-local-treestudyplan .q-student-results .q-condition-heading:not(:last-child),
.path-local-treestudyplan .q-student-results .q-period-results:not(:last-child),
.path-local-treestudyplan .q-student-results .q-line-results:not(:last-child),
.path-local-treestudyplan .q-student-results .q-item-results:not(:last-child),
.path-local-treestudyplan .q-student-results .q-condition-results:not(:last-child) {
border-right: 1px solid gray;
}
.path-local-treestudyplan .q-header .q-period-heading .q-header-title,
.path-local-treestudyplan .q-header .q-line-heading .q-header-title,
.path-local-treestudyplan .q-header .q-item-heading .q-header-title,
.path-local-treestudyplan .q-header .q-condition-heading .q-header-title,
.path-local-treestudyplan .q-header .q-period-results .q-header-title,
.path-local-treestudyplan .q-header .q-line-results .q-header-title,
.path-local-treestudyplan .q-header .q-item-results .q-header-title,
.path-local-treestudyplan .q-header .q-condition-results .q-header-title,
.path-local-treestudyplan .q-student-results .q-period-heading .q-header-title,
.path-local-treestudyplan .q-student-results .q-line-heading .q-header-title,
.path-local-treestudyplan .q-student-results .q-item-heading .q-header-title,
.path-local-treestudyplan .q-student-results .q-condition-heading .q-header-title,
.path-local-treestudyplan .q-student-results .q-period-results .q-header-title,
.path-local-treestudyplan .q-student-results .q-line-results .q-header-title,
.path-local-treestudyplan .q-student-results .q-item-results .q-header-title,
.path-local-treestudyplan .q-student-results .q-condition-results .q-header-title {
padding: 0.5rem;
}
.path-local-treestudyplan .q-header .q-period-heading .q-header-details,
.path-local-treestudyplan .q-header .q-period-heading .q-result-details,
.path-local-treestudyplan .q-header .q-line-heading .q-header-details,
.path-local-treestudyplan .q-header .q-line-heading .q-result-details,
.path-local-treestudyplan .q-header .q-item-heading .q-header-details,
.path-local-treestudyplan .q-header .q-item-heading .q-result-details,
.path-local-treestudyplan .q-header .q-condition-heading .q-header-details,
.path-local-treestudyplan .q-header .q-condition-heading .q-result-details,
.path-local-treestudyplan .q-header .q-period-results .q-header-details,
.path-local-treestudyplan .q-header .q-period-results .q-result-details,
.path-local-treestudyplan .q-header .q-line-results .q-header-details,
.path-local-treestudyplan .q-header .q-line-results .q-result-details,
.path-local-treestudyplan .q-header .q-item-results .q-header-details,
.path-local-treestudyplan .q-header .q-item-results .q-result-details,
.path-local-treestudyplan .q-header .q-condition-results .q-header-details,
.path-local-treestudyplan .q-header .q-condition-results .q-result-details,
.path-local-treestudyplan .q-student-results .q-period-heading .q-header-details,
.path-local-treestudyplan .q-student-results .q-period-heading .q-result-details,
.path-local-treestudyplan .q-student-results .q-line-heading .q-header-details,
.path-local-treestudyplan .q-student-results .q-line-heading .q-result-details,
.path-local-treestudyplan .q-student-results .q-item-heading .q-header-details,
.path-local-treestudyplan .q-student-results .q-item-heading .q-result-details,
.path-local-treestudyplan .q-student-results .q-condition-heading .q-header-details,
.path-local-treestudyplan .q-student-results .q-condition-heading .q-result-details,
.path-local-treestudyplan .q-student-results .q-period-results .q-header-details,
.path-local-treestudyplan .q-student-results .q-period-results .q-result-details,
.path-local-treestudyplan .q-student-results .q-line-results .q-header-details,
.path-local-treestudyplan .q-student-results .q-line-results .q-result-details,
.path-local-treestudyplan .q-student-results .q-item-results .q-header-details,
.path-local-treestudyplan .q-student-results .q-item-results .q-result-details,
.path-local-treestudyplan .q-student-results .q-condition-results .q-header-details,
.path-local-treestudyplan .q-student-results .q-condition-results .q-result-details {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
border-top: 1px solid green;
}
.path-local-treestudyplan .q-header .q-result,
.path-local-treestudyplan .q-student-results .q-result {
height: 2rem;
}
.path-local-treestudyplan .q-header,
.path-local-treestudyplan .q-student-results {
/*.q-line-heading > .q-header-title,*/
}
.path-local-treestudyplan .q-header .q-item-heading > .q-header-title,
.path-local-treestudyplan .q-header .q-condition-heading,
.path-local-treestudyplan .q-student-results .q-item-heading > .q-header-title,
.path-local-treestudyplan .q-student-results .q-condition-heading {
width: 2rem;
writing-mode: vertical-lr;
text-orientation: sideways;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.path-local-treestudyplan .q-header .q-line-heading > .q-header-title,
.path-local-treestudyplan .q-student-results .q-line-heading > .q-header-title {
height: fit-content;
}
.path-local-treestudyplan .q-header .q-item-heading > .q-item-heading,
.path-local-treestudyplan .q-student-results .q-item-heading > .q-item-heading {
height: 6rem;
}
.path-local-treestudyplan .q-header .q-condition-heading,
.path-local-treestudyplan .q-student-results .q-condition-heading {
height: 8rem;
}
.path-local-treestudyplan .q-studentname {
padding: 0.5em;
border-right: 2px solid grey;
width: 20rem;
}
.path-local-treestudyplan .q-studentname.heading {
font-weight: bold;
vertical-align: bottom;
display: flex;
}
.path-local-treestudyplan .q-studentname.heading span {
align-self: flex-end;
}
.path-local-treestudyplan .q-scrolly {
overflow-y: auto;
max-height: 300rem;
}
.path-local-treestudyplan .b-modal-justify-footer-between .modal-footer, .path-local-treestudyplan .b-modal-justify-footer-between .modal-footer,
.features-treestudyplan .b-modal-justify-footer-between .modal-footer { .features-treestudyplan .b-modal-justify-footer-between .modal-footer {