diff --git a/amd/build/dashboard_quiz_inprogress.min.js b/amd/build/dashboard_quiz_inprogress.min.js index 64726043..de18c4aa 100644 --- a/amd/build/dashboard_quiz_inprogress.min.js +++ b/amd/build/dashboard_quiz_inprogress.min.js @@ -1,2 +1,2 @@ -define ("local_assessfreq/dashboard_quiz_inprogress",["core/ajax","core/templates","core/fragment","local_assessfreq/zoom_modal","core/str","core/notification"],function(a,b,c,d,e,f){var g={},h,i=60,j,k="name_asc",l=[{cardId:"local-assessfreq-quiz-summary-upcomming-graph",call:"upcomming_quizzes",aspect:!0},{cardId:"local-assessfreq-quiz-summary-inprogress-graph",call:"all_participants_inprogress",aspect:!0}],m=function(b,c){return a.call([{methodname:"core_user_update_user_preferences",args:{preferences:[{type:b,value:c}]}}])[0]},n=function(b){return a.call([{methodname:"core_user_get_user_preferences",args:{name:b}}])[0]},o=function debouncer(a,b){var c;return function(){for(var d=arguments.length,e=Array(d),f=0;f.\n\n/**\n * Javascript for quizzes in progress display and processing.\n *\n * @package local_assessfreq\n * @copyright 2020 Matt Porritt \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['core/ajax', 'core/templates', 'core/fragment', 'local_assessfreq/zoom_modal', 'core/str', 'core/notification'],\nfunction(Ajax, Templates, Fragment, ZoomModal, Str, Notification) {\n\n /**\n * Module level variables.\n */\n var DashboardQuizInprogress = {};\n var contextid;\n var refreshPeriod = 60;\n var counterid;\n var tablesort = 'name_asc';\n\n const cards = [\n {cardId: 'local-assessfreq-quiz-summary-upcomming-graph', call: 'upcomming_quizzes', aspect: true},\n {cardId: 'local-assessfreq-quiz-summary-inprogress-graph', call: 'all_participants_inprogress', aspect: true}\n ];\n\n /**\n * Generic handler to persist user preferences.\n *\n * @param {string} type The name of the attribute you're updating\n * @param {string} value The value of the attribute you're updating\n * @return {object} jQuery promise\n */\n const setUserPreference = function(type, value) {\n var request = {\n methodname: 'core_user_update_user_preferences',\n args: {\n preferences: [{type: type, value: value}]\n }\n };\n\n return Ajax.call([request])[0];\n };\n\n /**\n * Generic handler to get user preference.\n *\n * @param {string} name The name of the attribute you're getting.\n * @return {object} jQuery promise\n */\n const getUserPreference = function(name) {\n var request = {\n methodname: 'core_user_get_user_preferences',\n args: {\n 'name': name\n }\n };\n\n return Ajax.call([request])[0];\n };\n\n /**\n * Quick and dirty debounce method for the settings.\n * This stops the ajax method that updates the table from being updated\n * while the user is still checking options.\n *\n */\n const debouncer = function (func, wait) {\n let timeout;\n\n return function executedFunction(...args) {\n const later = () => {\n clearTimeout(timeout);\n func(...args);\n };\n\n clearTimeout(timeout);\n timeout = setTimeout(later, wait);\n };\n };\n\n const debounceTable = debouncer(() => {\n getSummaryTable();\n }, 750);\n\n /**\n *\n */\n const refreshCounter = function(reset) {\n let progressElement = document.getElementById('local-assessfreq-period-progress');\n\n // Reset the current count process.\n if (reset == true) {\n clearInterval(counterid);\n counterid = null;\n progressElement.setAttribute('style', 'width: 100%');\n progressElement.setAttribute('aria-valuenow', 100);\n }\n\n // Exit early if there is already a counter running.\n if (counterid) {\n return;\n }\n\n counterid = setInterval(() => {\n let progressWidthAria = progressElement.getAttribute('aria-valuenow');\n const progressStep = 100 / refreshPeriod;\n\n if ((progressWidthAria - progressStep) > 0) {\n progressElement.setAttribute('style', 'width: ' + (progressWidthAria - progressStep) + '%');\n progressElement.setAttribute('aria-valuenow', (progressWidthAria - progressStep));\n } else {\n clearInterval(counterid);\n counterid = null;\n progressElement.setAttribute('style', 'width: 100%');\n progressElement.setAttribute('aria-valuenow', 100);\n processDashboard();\n refreshCounter();\n }\n }, (1000));\n };\n\n /**\n * Process the search events from the quiz table.\n */\n const tableSearch = function(event) {\n if (event.key === 'Meta' || event.ctrlKey) {\n return false;\n }\n\n if (event.target.value.length === 0 || event.target.value.length > 2) {\n debounceTable();\n }\n };\n\n /**\n * Process the search reset click event from the quiz table.\n */\n const tableSearchReset = function() {\n let tableSearchInputElement = document.getElementById('local-assessfreq-quiz-inprogress-table-search');\n tableSearchInputElement.value = '';\n tableSearchInputElement.focus();\n getSummaryTable();\n };\n\n /**\n * For each of the cards on the dashbaord get their corresponding chart data.\n * Data is based on the year variable from the corresponding dropdown.\n * Chart data is loaded via ajax.\n *\n */\n const getCardCharts = function() {\n cards.forEach((cardData) => {\n let cardElement = document.getElementById(cardData.cardId);\n let spinner = cardElement.getElementsByClassName('overlay-icon-container')[0];\n let chartbody = cardElement.getElementsByClassName('chart-body')[0];\n let params = {'data': JSON.stringify({'call': cardData.call})};\n\n spinner.classList.remove('hide'); // Show sinner if not already shown.\n Fragment.loadFragment('local_assessfreq', 'get_quiz_inprogress_chart', contextid, params)\n .done((response) => {\n let resObj = JSON.parse(response);\n if (resObj.hasdata == true) {\n let context = { 'withtable' : true, 'chartdata' : JSON.stringify(resObj.chart), 'aspect' : cardData.aspect};\n Templates.render('local_assessfreq/chart', context).done((html, js) => {\n spinner.classList.add('hide'); // Hide spinner if not already hidden.\n // Load card body.\n Templates.replaceNodeContents(chartbody, html, js);\n }).fail(() => {\n Notification.exception(new Error('Failed to load chart template.'));\n return;\n });\n return;\n } else {\n Str.get_string('nodata', 'local_assessfreq').then((str) => {\n const noDatastr = document.createElement('h3');\n noDatastr.innerHTML = str;\n chartbody.innerHTML = noDatastr.outerHTML;\n spinner.classList.add('hide'); // Hide spinner if not already hidden.\n return;\n }).catch(() => {\n Notification.exception(new Error('Failed to load string: nodata'));\n });\n }\n }).fail(() => {\n Notification.exception(new Error('Failed to load card.'));\n return;\n });\n });\n };\n\n /**\n * Process the nav event from the quiz table.\n */\n const tableNav = function(event) {\n event.preventDefault();\n\n const linkUrl = new URL(event.target.closest('a').href);\n const page = linkUrl.searchParams.get('page');\n\n if (page) {\n getSummaryTable(page);\n }\n };\n\n /**\n * Process the row set event from the quiz table.\n */\n const tableSearchRowSet = function(event) {\n event.preventDefault();\n if (event.target.tagName.toLowerCase() === 'a') {\n let rows = event.target.dataset.metric;\n setUserPreference('local_assessfreq_quiz_table_inprogress_preference', rows)\n .then(() => {\n getSummaryTable(); // Reload the table.\n })\n .fail(() => {\n Notification.exception(new Error('Failed to update user preference: rows'));\n });\n }\n };\n\n /**\n * Get and process the selected assessment metric from the dropdown for the heatmap display,\n * and update the corresponding user perference.\n *\n * @param {event} event The triggered event for the element.\n */\n const tableSortButtonAction = function(event) {\n event.preventDefault();\n var element = event.target;\n\n if (element.tagName.toLowerCase() === 'a' && element.dataset.sort != tablesort) {\n tablesort = element.dataset.sort;\n\n let links = element.parentNode.getElementsByTagName('a');\n for (let i = 0; i < links.length; i++) {\n links[i].classList.remove('active');\n }\n\n element.classList.add('active');\n\n // Save selection as a user preference.\n setUserPreference('local_assessfreq_quiz_table_inprogress_sort_preference', tablesort);\n\n debounceTable(); // Call function to update table.\n\n }\n };\n\n /**\n * Re-add event listeners when the quiz table is updated.\n */\n const tableEventListeners = function() {\n const tableElement = document.getElementById('local-assessfreq-quiz-inprogress-table');\n const tableNavElement = tableElement.querySelectorAll('nav'); // There are two nav paging elements per table.\n\n tableNavElement.forEach((navElement) => {\n navElement.addEventListener('click', tableNav);\n });\n };\n\n /**\n * Display the table that contains all in progress quiz summaries.\n */\n const getSummaryTable = function(page) {\n if (typeof page === \"undefined\") {\n page = 0;\n }\n\n let tableElement = document.getElementById('local-assessfreq-quiz-inprogress-table');\n let spinner = tableElement.getElementsByClassName('overlay-icon-container')[0];\n let tableBody = tableElement.getElementsByClassName('table-body')[0];\n let search = document.getElementById('local-assessfreq-quiz-inprogress-table-search').value.trim();\n let sortarray = tablesort.split('_');\n let sorton = sortarray[0];\n let direction = sortarray[1];\n\n let params = {'data': JSON.stringify({'search': search, 'page': page, 'sorton': sorton, 'direction': direction})};\n\n spinner.classList.remove('hide'); // Show sinner if not already shown.\n\n // Load table content.\n Fragment.loadFragment('local_assessfreq', 'get_quizzes_inprogress_table', contextid, params)\n .done((response, js) => {\n tableBody.innerHTML = response;\n Templates.runTemplateJS(js); // Magic call the initialises JS from template included in response template HTML.\n spinner.classList.add('hide'); // Hide spinner if not already hidden.\n tableEventListeners(); // Re-add table event listeners.\n\n }).fail(() => {\n Notification.exception(new Error('Failed to update table.'));\n return;\n });\n };\n\n /**\n * Starts the processing of the dashboard.\n */\n const processDashboard = function() {\n // Get summary quiz data.\n Ajax.call([{\n methodname: 'local_assessfreq_get_inprogress_counts',\n args: {},\n }])[0].then((response) => {\n let quizSummary = JSON.parse(response);\n let summaryElement = document.getElementById('local-assessfreq-quiz-dashboard-inprogress-summary-card');\n let summarySpinner = summaryElement.getElementsByClassName('overlay-icon-container')[0];\n let tableSearchInputElement = document.getElementById('local-assessfreq-quiz-inprogress-table-search');\n let tableSearchResetElement = document.getElementById('local-assessfreq-quiz-inprogress-table-search-reset');\n let tableSearchRowsElement = document.getElementById('local-assessfreq-quiz-inprogress-table-rows');\n let tableSortElement = document.getElementById('local-assessfreq-inprogress-table-sort');\n\n summaryElement.classList.remove('hide'); // Show the card.\n\n // Populate summary card with details.\n Templates.render('local_assessfreq/quiz-dashboard-inprogress-summary-card-content', quizSummary)\n .done((html) => {\n summarySpinner.classList.add('hide');\n\n let contentcontainer = document.getElementById('local-assessfreq-quiz-dashboard-inprogress-summary-card-content');\n Templates.replaceNodeContents(contentcontainer, html);\n }).fail(() => {\n Notification.exception(new Error('Failed to load quiz counts template.'));\n return;\n });\n\n getCardCharts();\n getSummaryTable();\n refreshCounter();\n\n // Table event listeners.\n tableSearchInputElement.addEventListener('keyup', tableSearch);\n tableSearchInputElement.addEventListener('paste', tableSearch);\n tableSearchResetElement.addEventListener('click', tableSearchReset);\n tableSearchRowsElement.addEventListener('click', tableSearchRowSet);\n tableSortElement.addEventListener('click', tableSortButtonAction);\n\n return;\n }).fail(() => {\n Notification.exception(new Error('Failed to get quiz summary counts'));\n });\n };\n\n /**\n * Handle processing of refresh and period button actions.\n */\n const refreshAction = function(event) {\n event.preventDefault();\n var element = event.target;\n\n if (element.closest('button') !== null && element.closest('button').id == 'local-assessfreq-refresh-quiz-dashboard') {\n refreshCounter(true);\n processDashboard();\n } else if (element.tagName.toLowerCase() === 'a') {\n refreshPeriod = element.dataset.period;\n refreshCounter(true);\n setUserPreference('local_assessfreq_quiz_refresh_preference', refreshPeriod);\n }\n };\n\n /**\n * Trigger the zoom graph. Thin wrapper to add extra data to click event.\n */\n const triggerZoomGraph = function(event) {\n let call = event.target.closest('div').dataset.call;\n let params = {'data': JSON.stringify({'call': call})};\n let method = 'get_quiz_inprogress_chart';\n\n ZoomModal.zoomGraph(event, params, method);\n };\n\n /**\n * Initialise method for quizzes in progress dashboard rendering.\n */\n DashboardQuizInprogress.init = function(context) {\n contextid = context;\n ZoomModal.init(context); // Create the zoom modal.\n\n getUserPreference('local_assessfreq_quiz_refresh_preference')\n .then((response) => {\n refreshPeriod = response.preferences[0].value ? response.preferences[0].value : 60;\n })\n .fail(() => {\n Notification.exception(new Error('Failed to get use preference: refresh'));\n });\n\n getUserPreference('local_assessfreq_quiz_table_inprogress_sort_preference')\n .then((response) => {\n tablesort = response.preferences[0].value ? response.preferences[0].value : 'name_asc';\n })\n .fail(() => {\n Notification.exception(new Error('Failed to get use preference: tablesort'));\n });\n\n // Event handling for refresh and period buttons.\n let refreshElement = document.getElementById('local-assessfreq-period-container');\n refreshElement.addEventListener('click', refreshAction);\n\n // Set up zoom event listeners.\n let summaryZoom = document.getElementById('local-assessfreq-quiz-summary-inprogress-graph-zoom');\n summaryZoom.addEventListener('click', triggerZoomGraph);\n\n let upcommingZoom = document.getElementById('local-assessfreq-quiz-summary-upcomming-graph-zoom');\n upcommingZoom.addEventListener('click', triggerZoomGraph);\n\n processDashboard();\n\n };\n\n return DashboardQuizInprogress;\n});\n"],"file":"dashboard_quiz_inprogress.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/dashboard_quiz_inprogress.js"],"names":["define","Ajax","Templates","Fragment","ZoomModal","Str","Notification","DashboardQuizInprogress","contextid","refreshPeriod","counterid","tablesort","hoursAhead","hoursBehind","cards","cardId","call","aspect","setUserPreference","type","value","methodname","args","preferences","getUserPreference","name","debounceTable","debouncer","func","wait","timeout","later","clearTimeout","setTimeout","getSummaryTable","refreshCounter","reset","progressElement","document","getElementById","clearInterval","setAttribute","setInterval","progressWidthAria","getAttribute","progressStep","processDashboard","tableSearch","event","key","ctrlKey","target","length","tableSearchReset","tableSearchInputElement","focus","getCardCharts","forEach","cardData","cardElement","spinner","getElementsByClassName","chartbody","params","JSON","stringify","classList","remove","loadFragment","done","response","resObj","parse","hasdata","context","chart","render","html","js","add","replaceNodeContents","fail","exception","Error","get_string","then","str","noDatastr","createElement","innerHTML","outerHTML","catch","tableNav","preventDefault","linkUrl","URL","closest","href","page","searchParams","get","tableSearchRowSet","tagName","toLowerCase","rows","dataset","metric","tableSortButtonAction","element","sort","links","parentNode","getElementsByTagName","i","tableEventListeners","tableElement","tableNavElement","querySelectorAll","navElement","addEventListener","tableBody","search","trim","sortarray","split","sorton","direction","runTemplateJS","quizSummary","summaryElement","summarySpinner","tableSearchResetElement","tableSearchRowsElement","tableSortElement","contentcontainer","refreshAction","id","period","triggerZoomGraph","zoomGraph","quizzesAheadSet","hours","quizzesBehindSet","init","refreshElement","summaryZoom","upcommingZoom","quizzesAheadElement","quizzesBehindElement"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Javascript for quizzes in progress display and processing.\n *\n * @package local_assessfreq\n * @copyright 2020 Matt Porritt \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['core/ajax', 'core/templates', 'core/fragment', 'local_assessfreq/zoom_modal', 'core/str', 'core/notification'],\nfunction(Ajax, Templates, Fragment, ZoomModal, Str, Notification) {\n\n /**\n * Module level variables.\n */\n var DashboardQuizInprogress = {};\n var contextid;\n var refreshPeriod = 60;\n var counterid;\n var tablesort = 'name_asc';\n var hoursAhead = 0;\n var hoursBehind = 0;\n\n const cards = [\n {cardId: 'local-assessfreq-quiz-summary-upcomming-graph', call: 'upcomming_quizzes', aspect: true},\n {cardId: 'local-assessfreq-quiz-summary-inprogress-graph', call: 'all_participants_inprogress', aspect: true}\n ];\n\n /**\n * Generic handler to persist user preferences.\n *\n * @param {string} type The name of the attribute you're updating\n * @param {string} value The value of the attribute you're updating\n * @return {object} jQuery promise\n */\n const setUserPreference = function(type, value) {\n var request = {\n methodname: 'core_user_update_user_preferences',\n args: {\n preferences: [{type: type, value: value}]\n }\n };\n\n return Ajax.call([request])[0];\n };\n\n /**\n * Generic handler to get user preference.\n *\n * @param {string} name The name of the attribute you're getting.\n * @return {object} jQuery promise\n */\n const getUserPreference = function(name) {\n var request = {\n methodname: 'core_user_get_user_preferences',\n args: {\n 'name': name\n }\n };\n\n return Ajax.call([request])[0];\n };\n\n /**\n * Quick and dirty debounce method for the settings.\n * This stops the ajax method that updates the table from being updated\n * while the user is still checking options.\n *\n */\n const debouncer = function (func, wait) {\n let timeout;\n\n return function executedFunction(...args) {\n const later = () => {\n clearTimeout(timeout);\n func(...args);\n };\n\n clearTimeout(timeout);\n timeout = setTimeout(later, wait);\n };\n };\n\n const debounceTable = debouncer(() => {\n getSummaryTable();\n }, 750);\n\n /**\n *\n */\n const refreshCounter = function(reset) {\n let progressElement = document.getElementById('local-assessfreq-period-progress');\n\n // Reset the current count process.\n if (reset == true) {\n clearInterval(counterid);\n counterid = null;\n progressElement.setAttribute('style', 'width: 100%');\n progressElement.setAttribute('aria-valuenow', 100);\n }\n\n // Exit early if there is already a counter running.\n if (counterid) {\n return;\n }\n\n counterid = setInterval(() => {\n let progressWidthAria = progressElement.getAttribute('aria-valuenow');\n const progressStep = 100 / refreshPeriod;\n\n if ((progressWidthAria - progressStep) > 0) {\n progressElement.setAttribute('style', 'width: ' + (progressWidthAria - progressStep) + '%');\n progressElement.setAttribute('aria-valuenow', (progressWidthAria - progressStep));\n } else {\n clearInterval(counterid);\n counterid = null;\n progressElement.setAttribute('style', 'width: 100%');\n progressElement.setAttribute('aria-valuenow', 100);\n processDashboard();\n refreshCounter();\n }\n }, (1000));\n };\n\n /**\n * Process the search events from the quiz table.\n */\n const tableSearch = function(event) {\n if (event.key === 'Meta' || event.ctrlKey) {\n return false;\n }\n\n if (event.target.value.length === 0 || event.target.value.length > 2) {\n debounceTable();\n }\n };\n\n /**\n * Process the search reset click event from the quiz table.\n */\n const tableSearchReset = function() {\n let tableSearchInputElement = document.getElementById('local-assessfreq-quiz-inprogress-table-search');\n tableSearchInputElement.value = '';\n tableSearchInputElement.focus();\n getSummaryTable();\n };\n\n /**\n * For each of the cards on the dashbaord get their corresponding chart data.\n * Data is based on the year variable from the corresponding dropdown.\n * Chart data is loaded via ajax.\n *\n */\n const getCardCharts = function() {\n cards.forEach((cardData) => {\n let cardElement = document.getElementById(cardData.cardId);\n let spinner = cardElement.getElementsByClassName('overlay-icon-container')[0];\n let chartbody = cardElement.getElementsByClassName('chart-body')[0];\n let params = {\n 'data': JSON.stringify({'call': cardData.call, 'hoursahead': hoursAhead, 'hoursbehind': hoursBehind\n })};\n\n spinner.classList.remove('hide'); // Show sinner if not already shown.\n Fragment.loadFragment('local_assessfreq', 'get_quiz_inprogress_chart', contextid, params)\n .done((response) => {\n let resObj = JSON.parse(response);\n if (resObj.hasdata == true) {\n let context = { 'withtable' : true, 'chartdata' : JSON.stringify(resObj.chart), 'aspect' : cardData.aspect};\n Templates.render('local_assessfreq/chart', context).done((html, js) => {\n spinner.classList.add('hide'); // Hide spinner if not already hidden.\n // Load card body.\n Templates.replaceNodeContents(chartbody, html, js);\n }).fail(() => {\n Notification.exception(new Error('Failed to load chart template.'));\n return;\n });\n return;\n } else {\n Str.get_string('nodata', 'local_assessfreq').then((str) => {\n const noDatastr = document.createElement('h3');\n noDatastr.innerHTML = str;\n chartbody.innerHTML = noDatastr.outerHTML;\n spinner.classList.add('hide'); // Hide spinner if not already hidden.\n return;\n }).catch(() => {\n Notification.exception(new Error('Failed to load string: nodata'));\n });\n }\n }).fail(() => {\n Notification.exception(new Error('Failed to load card.'));\n return;\n });\n });\n };\n\n /**\n * Process the nav event from the quiz table.\n */\n const tableNav = function(event) {\n event.preventDefault();\n\n const linkUrl = new URL(event.target.closest('a').href);\n const page = linkUrl.searchParams.get('page');\n\n if (page) {\n getSummaryTable(page);\n }\n };\n\n /**\n * Process the row set event from the quiz table.\n */\n const tableSearchRowSet = function(event) {\n event.preventDefault();\n if (event.target.tagName.toLowerCase() === 'a') {\n let rows = event.target.dataset.metric;\n setUserPreference('local_assessfreq_quiz_table_inprogress_preference', rows)\n .then(() => {\n getSummaryTable(); // Reload the table.\n })\n .fail(() => {\n Notification.exception(new Error('Failed to update user preference: rows'));\n });\n }\n };\n\n /**\n * Get and process the selected assessment metric from the dropdown for the heatmap display,\n * and update the corresponding user perference.\n *\n * @param {event} event The triggered event for the element.\n */\n const tableSortButtonAction = function(event) {\n event.preventDefault();\n var element = event.target;\n\n if (element.tagName.toLowerCase() === 'a' && element.dataset.sort != tablesort) {\n tablesort = element.dataset.sort;\n\n let links = element.parentNode.getElementsByTagName('a');\n for (let i = 0; i < links.length; i++) {\n links[i].classList.remove('active');\n }\n\n element.classList.add('active');\n\n // Save selection as a user preference.\n setUserPreference('local_assessfreq_quiz_table_inprogress_sort_preference', tablesort);\n\n debounceTable(); // Call function to update table.\n\n }\n };\n\n /**\n * Re-add event listeners when the quiz table is updated.\n */\n const tableEventListeners = function() {\n const tableElement = document.getElementById('local-assessfreq-quiz-inprogress-table');\n const tableNavElement = tableElement.querySelectorAll('nav'); // There are two nav paging elements per table.\n\n tableNavElement.forEach((navElement) => {\n navElement.addEventListener('click', tableNav);\n });\n };\n\n /**\n * Display the table that contains all in progress quiz summaries.\n */\n const getSummaryTable = function(page) {\n if (typeof page === \"undefined\") {\n page = 0;\n }\n\n let tableElement = document.getElementById('local-assessfreq-quiz-inprogress-table');\n let spinner = tableElement.getElementsByClassName('overlay-icon-container')[0];\n let tableBody = tableElement.getElementsByClassName('table-body')[0];\n let search = document.getElementById('local-assessfreq-quiz-inprogress-table-search').value.trim();\n let sortarray = tablesort.split('_');\n let sorton = sortarray[0];\n let direction = sortarray[1];\n\n let params = {'data': JSON.stringify(\n {'search': search, 'page': page, 'sorton': sorton, 'direction': direction,\n 'hoursahead': hoursAhead, 'hoursbehind': hoursBehind}\n )};\n\n spinner.classList.remove('hide'); // Show sinner if not already shown.\n\n // Load table content.\n Fragment.loadFragment('local_assessfreq', 'get_quizzes_inprogress_table', contextid, params)\n .done((response, js) => {\n tableBody.innerHTML = response;\n Templates.runTemplateJS(js); // Magic call the initialises JS from template included in response template HTML.\n spinner.classList.add('hide'); // Hide spinner if not already hidden.\n tableEventListeners(); // Re-add table event listeners.\n\n }).fail(() => {\n Notification.exception(new Error('Failed to update table.'));\n return;\n });\n };\n\n /**\n * Starts the processing of the dashboard.\n */\n const processDashboard = function() {\n // Get summary quiz data.\n Ajax.call([{\n methodname: 'local_assessfreq_get_inprogress_counts',\n args: {},\n }])[0].then((response) => {\n let quizSummary = JSON.parse(response);\n let summaryElement = document.getElementById('local-assessfreq-quiz-dashboard-inprogress-summary-card');\n let summarySpinner = summaryElement.getElementsByClassName('overlay-icon-container')[0];\n let tableSearchInputElement = document.getElementById('local-assessfreq-quiz-inprogress-table-search');\n let tableSearchResetElement = document.getElementById('local-assessfreq-quiz-inprogress-table-search-reset');\n let tableSearchRowsElement = document.getElementById('local-assessfreq-quiz-inprogress-table-rows');\n let tableSortElement = document.getElementById('local-assessfreq-inprogress-table-sort');\n\n summaryElement.classList.remove('hide'); // Show the card.\n\n // Populate summary card with details.\n Templates.render('local_assessfreq/quiz-dashboard-inprogress-summary-card-content', quizSummary)\n .done((html) => {\n summarySpinner.classList.add('hide');\n\n let contentcontainer = document.getElementById('local-assessfreq-quiz-dashboard-inprogress-summary-card-content');\n Templates.replaceNodeContents(contentcontainer, html);\n }).fail(() => {\n Notification.exception(new Error('Failed to load quiz counts template.'));\n return;\n });\n\n getCardCharts();\n getSummaryTable();\n refreshCounter();\n\n // Table event listeners.\n tableSearchInputElement.addEventListener('keyup', tableSearch);\n tableSearchInputElement.addEventListener('paste', tableSearch);\n tableSearchResetElement.addEventListener('click', tableSearchReset);\n tableSearchRowsElement.addEventListener('click', tableSearchRowSet);\n tableSortElement.addEventListener('click', tableSortButtonAction);\n\n return;\n }).fail(() => {\n Notification.exception(new Error('Failed to get quiz summary counts'));\n });\n };\n\n /**\n * Handle processing of refresh and period button actions.\n */\n const refreshAction = function(event) {\n event.preventDefault();\n var element = event.target;\n\n if (element.closest('button') !== null && element.closest('button').id == 'local-assessfreq-refresh-quiz-dashboard') {\n refreshCounter(true);\n processDashboard();\n } else if (element.tagName.toLowerCase() === 'a') {\n refreshPeriod = element.dataset.period;\n refreshCounter(true);\n setUserPreference('local_assessfreq_quiz_refresh_preference', refreshPeriod);\n }\n };\n\n /**\n * Trigger the zoom graph. Thin wrapper to add extra data to click event.\n */\n const triggerZoomGraph = function(event) {\n let call = event.target.closest('div').dataset.call;\n let params = {'data': JSON.stringify({'call': call})};\n let method = 'get_quiz_inprogress_chart';\n\n ZoomModal.zoomGraph(event, params, method);\n };\n\n /**\n * Process the hours ahead event from the in progress quizzes table.\n */\n const quizzesAheadSet = function(event) {\n event.preventDefault();\n if (event.target.tagName.toLowerCase() === 'a') {\n let hours = event.target.dataset.metric;\n setUserPreference('local_assessfreq_quizzes_inprogress_table_hoursahead_preference', hours)\n .then(() => {\n hoursAhead = hours;\n processDashboard(); // Reload the table.\n })\n .fail(() => {\n Notification.exception(new Error('Failed to update user preference: hours ahead'));\n });\n }\n };\n\n /**\n * Process the hours behind event from the in progress quizzes table.\n */\n const quizzesBehindSet = function(event) {\n event.preventDefault();\n if (event.target.tagName.toLowerCase() === 'a') {\n let hours = event.target.dataset.metric;\n setUserPreference('local_assessfreq_quizzes_inprogress_table_hoursbehind_preference', hours)\n .then(() => {\n hoursBehind = hours;\n processDashboard(); // Reload the table.\n })\n .fail(() => {\n Notification.exception(new Error('Failed to update user preference: hours behind'));\n });\n }\n };\n\n /**\n * Initialise method for quizzes in progress dashboard rendering.\n */\n DashboardQuizInprogress.init = function(context) {\n contextid = context;\n ZoomModal.init(context); // Create the zoom modal.\n\n getUserPreference('local_assessfreq_quiz_refresh_preference')\n .then((response) => {\n refreshPeriod = response.preferences[0].value ? response.preferences[0].value : 60;\n })\n .fail(() => {\n Notification.exception(new Error('Failed to get use preference: refresh'));\n });\n\n getUserPreference('local_assessfreq_quiz_table_inprogress_sort_preference')\n .then((response) => {\n tablesort = response.preferences[0].value ? response.preferences[0].value : 'name_asc';\n })\n .fail(() => {\n Notification.exception(new Error('Failed to get use preference: tablesort'));\n });\n\n getUserPreference('local_assessfreq_quizzes_inprogress_table_hoursahead_preference')\n .then((response) => {\n hoursAhead = response.preferences[0].value ? response.preferences[0].value : 0;\n })\n .fail(() => {\n Notification.exception(new Error('Failed to get use preference: hoursahead'));\n });\n\n getUserPreference('local_assessfreq_quizzes_inprogress_table_hoursbehind_preference')\n .then((response) => {\n hoursBehind = response.preferences[0].value ? response.preferences[0].value : 0;\n })\n .fail(() => {\n Notification.exception(new Error('Failed to get use preference: hoursbehind'));\n });\n\n // Event handling for refresh and period buttons.\n let refreshElement = document.getElementById('local-assessfreq-period-container');\n refreshElement.addEventListener('click', refreshAction);\n\n // Set up zoom event listeners.\n let summaryZoom = document.getElementById('local-assessfreq-quiz-summary-inprogress-graph-zoom');\n summaryZoom.addEventListener('click', triggerZoomGraph);\n\n let upcommingZoom = document.getElementById('local-assessfreq-quiz-summary-upcomming-graph-zoom');\n upcommingZoom.addEventListener('click', triggerZoomGraph);\n\n // Set up behind and ahead quizzes event listeners.\n let quizzesAheadElement = document.getElementById('local-assessfreq-quiz-student-table-hoursahead');\n quizzesAheadElement.addEventListener('click', quizzesAheadSet);\n\n let quizzesBehindElement = document.getElementById('local-assessfreq-quiz-student-table-hoursbehind');\n quizzesBehindElement.addEventListener('click', quizzesBehindSet);\n\n processDashboard();\n\n };\n\n return DashboardQuizInprogress;\n});\n"],"file":"dashboard_quiz_inprogress.min.js"} \ No newline at end of file diff --git a/amd/src/dashboard_quiz_inprogress.js b/amd/src/dashboard_quiz_inprogress.js index 0626be9f..8d9558e6 100644 --- a/amd/src/dashboard_quiz_inprogress.js +++ b/amd/src/dashboard_quiz_inprogress.js @@ -32,6 +32,8 @@ function(Ajax, Templates, Fragment, ZoomModal, Str, Notification) { var refreshPeriod = 60; var counterid; var tablesort = 'name_asc'; + var hoursAhead = 0; + var hoursBehind = 0; const cards = [ {cardId: 'local-assessfreq-quiz-summary-upcomming-graph', call: 'upcomming_quizzes', aspect: true}, @@ -168,7 +170,9 @@ function(Ajax, Templates, Fragment, ZoomModal, Str, Notification) { let cardElement = document.getElementById(cardData.cardId); let spinner = cardElement.getElementsByClassName('overlay-icon-container')[0]; let chartbody = cardElement.getElementsByClassName('chart-body')[0]; - let params = {'data': JSON.stringify({'call': cardData.call})}; + let params = { + 'data': JSON.stringify({'call': cardData.call, 'hoursahead': hoursAhead, 'hoursbehind': hoursBehind + })}; spinner.classList.remove('hide'); // Show sinner if not already shown. Fragment.loadFragment('local_assessfreq', 'get_quiz_inprogress_chart', contextid, params) @@ -290,7 +294,10 @@ function(Ajax, Templates, Fragment, ZoomModal, Str, Notification) { let sorton = sortarray[0]; let direction = sortarray[1]; - let params = {'data': JSON.stringify({'search': search, 'page': page, 'sorton': sorton, 'direction': direction})}; + let params = {'data': JSON.stringify( + {'search': search, 'page': page, 'sorton': sorton, 'direction': direction, + 'hoursahead': hoursAhead, 'hoursbehind': hoursBehind} + )}; spinner.classList.remove('hide'); // Show sinner if not already shown. @@ -384,6 +391,42 @@ function(Ajax, Templates, Fragment, ZoomModal, Str, Notification) { ZoomModal.zoomGraph(event, params, method); }; + /** + * Process the hours ahead event from the in progress quizzes table. + */ + const quizzesAheadSet = function(event) { + event.preventDefault(); + if (event.target.tagName.toLowerCase() === 'a') { + let hours = event.target.dataset.metric; + setUserPreference('local_assessfreq_quizzes_inprogress_table_hoursahead_preference', hours) + .then(() => { + hoursAhead = hours; + processDashboard(); // Reload the table. + }) + .fail(() => { + Notification.exception(new Error('Failed to update user preference: hours ahead')); + }); + } + }; + + /** + * Process the hours behind event from the in progress quizzes table. + */ + const quizzesBehindSet = function(event) { + event.preventDefault(); + if (event.target.tagName.toLowerCase() === 'a') { + let hours = event.target.dataset.metric; + setUserPreference('local_assessfreq_quizzes_inprogress_table_hoursbehind_preference', hours) + .then(() => { + hoursBehind = hours; + processDashboard(); // Reload the table. + }) + .fail(() => { + Notification.exception(new Error('Failed to update user preference: hours behind')); + }); + } + }; + /** * Initialise method for quizzes in progress dashboard rendering. */ @@ -407,6 +450,22 @@ function(Ajax, Templates, Fragment, ZoomModal, Str, Notification) { Notification.exception(new Error('Failed to get use preference: tablesort')); }); + getUserPreference('local_assessfreq_quizzes_inprogress_table_hoursahead_preference') + .then((response) => { + hoursAhead = response.preferences[0].value ? response.preferences[0].value : 0; + }) + .fail(() => { + Notification.exception(new Error('Failed to get use preference: hoursahead')); + }); + + getUserPreference('local_assessfreq_quizzes_inprogress_table_hoursbehind_preference') + .then((response) => { + hoursBehind = response.preferences[0].value ? response.preferences[0].value : 0; + }) + .fail(() => { + Notification.exception(new Error('Failed to get use preference: hoursbehind')); + }); + // Event handling for refresh and period buttons. let refreshElement = document.getElementById('local-assessfreq-period-container'); refreshElement.addEventListener('click', refreshAction); @@ -418,6 +477,13 @@ function(Ajax, Templates, Fragment, ZoomModal, Str, Notification) { let upcommingZoom = document.getElementById('local-assessfreq-quiz-summary-upcomming-graph-zoom'); upcommingZoom.addEventListener('click', triggerZoomGraph); + // Set up behind and ahead quizzes event listeners. + let quizzesAheadElement = document.getElementById('local-assessfreq-quiz-student-table-hoursahead'); + quizzesAheadElement.addEventListener('click', quizzesAheadSet); + + let quizzesBehindElement = document.getElementById('local-assessfreq-quiz-student-table-hoursbehind'); + quizzesBehindElement.addEventListener('click', quizzesBehindSet); + processDashboard(); }; diff --git a/classes/output/all_participants_inprogress.php b/classes/output/all_participants_inprogress.php index 674ff0eb..df1b8534 100644 --- a/classes/output/all_participants_inprogress.php +++ b/classes/output/all_participants_inprogress.php @@ -44,18 +44,34 @@ class all_participants_inprogress { * @param int $now Timestamp to get chart data for. * @return array With Generated chart object and chart data status. */ - public function get_all_participants_inprogress_chart(int $now): array { + public function get_all_participants_inprogress_chart(int $now, int $hoursahead = 0, int $hoursbehind = 0): array { // Get quizzes for the supplied timestamp. - $quiz = new quiz(); + $quiz = new quiz($hoursahead, $hoursbehind); $quizzes = $quiz->get_quiz_summaries($now); + $inprogressquizzes = $quizzes['inprogress']; + $upcommingquizzes = $quizzes['upcomming']; + $finishedquizzes = $quizzes['finished']; + + foreach ($upcommingquizzes as $timestamp => $upcommingquiz) { + foreach ($upcommingquiz as $timestampupcomming => $upcomming) { + $inprogressquizzes[$timestampupcomming] = $upcomming; + } + } + + foreach ($finishedquizzes as $timestamp => $finishedquiz) { + foreach ($finishedquiz as $timestampfinished => $finished) { + $inprogressquizzes[$timestampfinished] = $finished; + } + } + $notloggedin = 0; $loggedin = 0; $inprogress = 0; $finished = 0; - foreach ($quizzes['inprogress'] as $quizobj) { + foreach ($inprogressquizzes as $quizobj) { if (!empty($quizobj->tracking)) { $notloggedin += $quizobj->tracking->notloggedin; $loggedin += $quizobj->tracking->loggedin; diff --git a/classes/output/renderer.php b/classes/output/renderer.php index 3b9ffcce..f53dcb80 100644 --- a/classes/output/renderer.php +++ b/classes/output/renderer.php @@ -137,14 +137,31 @@ public function render_student_search_table( * @param string $direction The direction to sort the quizzes. * @return string $output HTML for the table. */ - public function render_quizzes_inprogress_table(string $search, int $page, string $sorton, string $direction): string { + public function render_quizzes_inprogress_table(string $search, int $page, string $sorton, string $direction, + int $hoursahead = 0, int $hoursbehind = 0): string { $context = \context_system::instance(); // TODO: pass the actual context in from the caller. $now = time(); - $quiz = new quiz(); + $quiz = new quiz($hoursahead, $hoursbehind); $quizzes = $quiz->get_quiz_summaries($now); $pagesize = get_user_preferences('local_assessfreq_quiz_table_inprogress_preference', 5); - list($filtered, $totalrows) = $quiz->filter_quizzes($quizzes['inprogress'], $search, $page, $pagesize); + $inprogressquizzes = $quizzes['inprogress']; + $upcommingquizzes = $quizzes['upcomming']; + $finishedquizzes = $quizzes['finished']; + + foreach ($upcommingquizzes as $key => $upcommingquiz) { + foreach ($upcommingquiz as $keyupcomming => $upcomming) { + $inprogressquizzes[$keyupcomming] = $upcomming; + } + } + + foreach ($finishedquizzes as $key => $finishedquiz) { + foreach ($finishedquiz as $keyfinished => $finished) { + $inprogressquizzes[$keyfinished] = $finished; + } + } + + list($filtered, $totalrows) = $quiz->filter_quizzes($inprogressquizzes, $search, $page, $pagesize); $sortedquizzes = \local_assessfreq\utils::sort($filtered, $sorton, $direction); $pagingbar = new \paging_bar($totalrows, $page, $pagesize, '/'); @@ -282,6 +299,9 @@ private function render_quiz_select_refresh_button(): string { */ private function render_quiz_refresh_button(): string { $preferencerefresh = get_user_preferences('local_assessfreq_quiz_refresh_preference', 60); + $preferencehoursahead = get_user_preferences('local_assessfreq_quizzes_inprogress_table_hoursahead_preference', 0); + $preferencehoursbehind = get_user_preferences('local_assessfreq_quizzes_inprogress_table_hoursbehind_preference', 0); + $refreshminutes = array( 60 => 'minuteone', 120 => 'minutetwo', @@ -289,9 +309,18 @@ private function render_quiz_refresh_button(): string { 600 => 'minuteten', ); + $hours = array( + 0 => 'hours0', + 1 => 'hours1', + 4 => 'hours4', + 8 => 'hours8', + ); + $context = array( 'refreshinitial' => get_string($refreshminutes[$preferencerefresh], 'local_assessfreq'), 'refresh' => array($refreshminutes[$preferencerefresh] => 'true'), + 'hoursahead' => array($hours[$preferencehoursahead] => 'true'), + 'hoursbehind' => array($hours[$preferencehoursbehind] => 'true'), ); return $this->render_from_template('local_assessfreq/quiz-dashboard-inprogress-controls', $context); @@ -364,6 +393,7 @@ private function render_student_table_cards(): string { ); $hours = array( + 0 => 'hours0', 1 => 'hours1', 4 => 'hours4', 8 => 'hours8', diff --git a/classes/output/upcomming_quizzes.php b/classes/output/upcomming_quizzes.php index a5650b5b..f07da54f 100644 --- a/classes/output/upcomming_quizzes.php +++ b/classes/output/upcomming_quizzes.php @@ -68,9 +68,17 @@ public function get_upcomming_quizzes_chart(int $now): array { $participantcount += $quiz->participants; } + // Check if inprogress quizzes are upcomming quizzes with overrides. + foreach ($quizzes['inprogress'] as $inprogress) { + if ($inprogress->timestampopen >= $timestamp && $inprogress->timestampopen < $timestamp + HOURSECS) { + $quizcount++; + $participantcount += $inprogress->participants; + } + } + $quizseriesdata[] = $quizcount; $participantseriesdata[] = $participantcount; - $labels[] = userdate($timestamp, get_string('inprogressdatetime', 'local_assessfreq')); + $labels[] = userdate($timestamp + HOURSECS, get_string('inprogressdatetime', 'local_assessfreq')); } // Create chart object. diff --git a/classes/quiz.php b/classes/quiz.php index fc9b3f73..4a9213f1 100644 --- a/classes/quiz.php +++ b/classes/quiz.php @@ -478,6 +478,10 @@ public function get_quiz_summaries(int $now): array { $quizdata->overrides = $quiz->overrides; } + // Get tracked users for quiz. + $trackedrecords = $this->get_quiz_tracking($quiz->id); + $quizdata->tracking = array_pop($trackedrecords); + $quizzes['upcomming'][$time][$quiz->id] = $quizdata; unset($trackedquizzes[$quiz->id]); } else { @@ -492,6 +496,10 @@ public function get_quiz_summaries(int $now): array { $quizdata->overrides = $quiz->overrides; } + // Get tracked users for quiz. + $trackedrecords = $this->get_quiz_tracking($quiz->id); + $quizdata->tracking = array_pop($trackedrecords); + $quizzes['inprogress'][$quiz->id] = $quizdata; unset($trackedquizzes[$quiz->id]); } @@ -519,6 +527,10 @@ public function get_quiz_summaries(int $now): array { $quizdata->overrides = $quiz->overrides; } + // Get tracked users for quiz. + $trackedrecords = $this->get_quiz_tracking($quiz->id); + $quizdata->tracking = array_pop($trackedrecords); + $quizzes['finished'][$time][$quiz->id] = $quizdata; unset($trackedquizzes[$quiz->id]); } diff --git a/lang/en/local_assessfreq.php b/lang/en/local_assessfreq.php index 6721d627..6e2f788a 100644 --- a/lang/en/local_assessfreq.php +++ b/lang/en/local_assessfreq.php @@ -65,6 +65,7 @@ $string['entersearchquiz'] = 'Search by quiz or course name'; $string['findcourse'] = 'Find course'; $string['finished'] = 'Finished'; +$string['hours0'] = 'Now'; $string['hours1'] = '1 Hour'; $string['hours4'] = '4 Hours'; $string['hours8'] = '8 Hours'; diff --git a/lib.php b/lib.php index 0569d8f9..efd6f297 100644 --- a/lib.php +++ b/lib.php @@ -86,6 +86,18 @@ function local_assessfreq_user_preferences() { 'type' => PARAM_INT ); + $preferences['local_assessfreq_quizzes_inprogress_table_hoursahead_preference'] = array( + 'null' => NULL_NOT_ALLOWED, + 'default' => 0, + 'type' => PARAM_INT + ); + + $preferences['local_assessfreq_quizzes_inprogress_table_hoursbehind_preference'] = array( + 'null' => NULL_NOT_ALLOWED, + 'default' => 0, + 'type' => PARAM_INT + ); + $preferences['local_assessfreq_quiz_table_inprogress_preference'] = array( 'null' => NULL_NOT_ALLOWED, 'default' => 20, @@ -187,7 +199,12 @@ function local_assessfreq_output_fragment_get_quiz_inprogress_chart($args): stri $assesschart = new $classname(); $now = time(); - $chart = $assesschart->$methodname($now); + + if ($methodname == 'get_all_participants_inprogress_chart') { + $chart = $assesschart->$methodname($now, $data->hoursahead, $data->hoursbehind); + } else { + $chart = $assesschart->$methodname($now); + } $chartdata = json_encode($chart); return $chartdata; @@ -280,9 +297,11 @@ function local_assessfreq_output_fragment_get_quizzes_inprogress_table($args): s $search = is_null($data->search) ? '' : $data->search; $sorton = is_null($data->sorton) ? 'name' : $data->sorton; $direction = is_null($data->direction) ? 'asc' : $data->direction; + $hoursahead = (int)$data->hoursahead; + $hoursbehind = (int)$data->hoursbehind; $output = $PAGE->get_renderer('local_assessfreq'); - $o = $output->render_quizzes_inprogress_table($search, $data->page, $sorton, $direction); + $o = $output->render_quizzes_inprogress_table($search, $data->page, $sorton, $direction, $hoursahead, $hoursbehind); return $o; } diff --git a/templates/nav-quiz-table-hoursahead-filter.mustache b/templates/nav-quiz-table-hoursahead-filter.mustache index ac98e3b6..f908cd66 100644 --- a/templates/nav-quiz-table-hoursahead-filter.mustache +++ b/templates/nav-quiz-table-hoursahead-filter.mustache @@ -28,6 +28,7 @@ aria-label="{{# str }} hoursahead, local_assessfreq {{/ str }}" aria-controls="hoursaheadfilter"> {{#pix}} i/next {{/pix}} {{# str }} hoursahead, local_assessfreq {{/ str }} + {{# str }} hours0, local_assessfreq {{/ str }} {{# str }} hours1, local_assessfreq {{/ str }} {{# str }} hours4, local_assessfreq {{/ str }} {{# str }} hours8, local_assessfreq {{/ str }} @@ -35,6 +36,15 @@