diff --git a/amd/build/dashboard.min.js b/amd/build/dashboard.min.js index 92d71ce..7089767 100644 --- a/amd/build/dashboard.min.js +++ b/amd/build/dashboard.min.js @@ -1,3 +1,3 @@ -define("local_sitsgradepush/dashboard",["exports","./sitsgradepush_helper","./progress","core/notification","core/modal_factory","core/modal_events"],(function(_exports,_sitsgradepush_helper,_progress,_notification,_modal_factory,_modal_events){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=_interopRequireDefault(_notification),_modal_factory=_interopRequireDefault(_modal_factory),_modal_events=_interopRequireDefault(_modal_events);let updatePageIntervalId=null,syncThreshold=30,globalCourseid=null,async=null;async function pushMarks(button){try{let assessmentmappingid=button.getAttribute("data-assessmentmappingid"),result=await(0,_sitsgradepush_helper.schedulePushTask)(assessmentmappingid);if(result.success){let tooltipid=button.getAttribute("aria-describedby");null!==tooltipid&&null!==document.getElementById(tooltipid)&&document.getElementById(tooltipid).remove()}else showErrorMessageForButton(button,result.message);return result}catch(error){return window.console.error(error),!1}}async function updateAssessments(courseid){let update=await(0,_sitsgradepush_helper.getAssessmentsUpdate)(courseid);if(update.success){let assessments=JSON.parse(update.assessments);assessments.length>0&&(function(assessments){let assessmentsHasTasks=assessments.filter((assessment=>null!==assessment.task)),assessmentIds=new Set(assessmentsHasTasks.map((item=>item.assessmentmappingid)));(function(assessmentsHasTasks,assessmentIds){document.querySelectorAll(".progress.async").forEach((progressBar=>{assessmentIds.has(progressBar.getAttribute("data-assessmentmappingid"))||progressBar.remove()})),assessmentsHasTasks.forEach((assessment=>{let progressBarId="progress-bar-"+assessment.task.assessmentmappingid,progressBar=document.getElementById(progressBarId),task=assessment.task;if(progressBar)(0,_progress.updateProgressBar)(progressBar,task.progress);else{progressBar=(0,_progress.createProgressBar)(progressBarId,"async",task.assessmentmappingid,task.progress),document.querySelector('.push-mark-button[data-assessmentmappingid="'+task.assessmentmappingid+'"]').parentNode.parentNode.insertAdjacentElement("afterend",progressBar)}}))})(assessmentsHasTasks,assessmentIds),function(assessmentIds){document.querySelectorAll(".push-mark-button").forEach((function(pushButton){let assessmentmappingid=pushButton.getAttribute("data-assessmentmappingid");if(assessmentIds.has(assessmentmappingid)){let spinner=(0,_progress.createSpinner)("text-light","spinner-border-sm");pushButton.innerHTML=spinner.outerHTML,pushButton.disabled=!0}else null!==assessmentmappingid?(pushButton.innerHTML='',pushButton.disabled=!1):pushButton.disabled=!0}))}(assessmentIds)}(assessments),function(assessments){let pushButtons=document.querySelectorAll(".push-mark-button"),assessmentsHasTransferRecords=assessments.filter((update=>1===update.transferrecords)),assessmentIds=new Set(assessmentsHasTransferRecords.map((assessment=>assessment.assessmentmappingid)));pushButtons.forEach((function(button){let assessmentmappingid=button.getAttribute("data-assessmentmappingid"),icon=button.parentNode.parentNode.querySelector(".records-icon");assessmentIds.has(assessmentmappingid)?icon.classList.contains("fa-circle-info")&&(icon.classList.replace("fa-solid","fa-regular"),icon.classList.replace("fa-circle-info","fa-file-lines")):icon.classList.contains("fa-file-lines")&&(icon.classList.replace("fa-regular","fa-solid"),icon.classList.replace("fa-file-lines","fa-circle-info"))}))}(assessments))}else clearInterval(updatePageIntervalId),window.console.error(update.message)}function showErrorMessageForButton(button,message){let errormessagerow=document.createElement("tr");errormessagerow.setAttribute("class","error-message-row"),errormessagerow.innerHTML='";let currentrow=button.closest("tr");null!==currentrow.nextElementSibling&¤trow.nextElementSibling.classList.contains("error-message-row")&¤trow.nextElementSibling.remove(),currentrow.insertAdjacentElement("afterend",errormessagerow)}function getPagePosition(page){return page instanceof Window?page.scrollY:page.scrollTop}_exports.init=(courseid,syncThresholdConfig,asyncConfig,moodleVersion)=>{let page;!function(){let successMessage=localStorage.getItem("successMessage");successMessage&&(_notification.default.addNotification({message:successMessage,type:"success"}),localStorage.removeItem("successMessage"))}(),syncThreshold=syncThresholdConfig,globalCourseid=courseid,async=asyncConfig,page=moodleVersion>"2023100900"?window:document.getElementById("page");let tableSelector=function(page){let tableSelector=document.getElementById("module-delivery-selector");return tableSelector.addEventListener("change",(function(){let selectedTable=document.getElementById(tableSelector.value),pagePosition=getPagePosition(page);if(selectedTable){let offset=-100,scrollPosition=pagePosition+selectedTable.getBoundingClientRect().top+offset;page.scrollTo({top:scrollPosition,behavior:"smooth"})}})),tableSelector}(page);!function(page,tableSelector){let backToTopButton=document.getElementById("backToTopButton");page.addEventListener("scroll",(function(){getPagePosition(page)>=100?backToTopButton.style.display="block":backToTopButton.style.display="none"})),backToTopButton.addEventListener("click",(function(){page.scrollTo({top:0,behavior:"smooth"}),tableSelector.selectedIndex=0}))}(page,tableSelector),function(){let changesourcebuttons=document.querySelectorAll(".change-source-button:not([disabled])");changesourcebuttons.length>0&&changesourcebuttons.forEach((function(button){button.addEventListener("click",(function(){window.location.href=button.getAttribute("data-url")}))}))}(),function(page,courseid){let mabpushbuttons=document.querySelectorAll(".push-mark-button");mabpushbuttons.length>0&&mabpushbuttons.forEach((function(button){let studentcount=button.getAttribute("data-numberofstudents"),assessmentmappingid=button.getAttribute("data-assessmentmappingid");null!==assessmentmappingid?button.addEventListener("click",(async function(){if("0"!==studentcount)if(studentcount>0&&studentcount0){let progressbar=(0,_progress.createProgressBar)("dashboard-progress-bar-sync","sync",assessmentmappingid,0,!0),modal=await _modal_factory.default.create({type:_modal_factory.default.types.ALERT,title:"Transferring Marks",body:'
'+progressbar.outerHTML,buttons:{cancel:"Cancel"}});await modal.show();let isModalVisible=!0,modalProgressbar=document.getElementById("dashboard-progress-bar-sync");modal.getRoot().on(_modal_events.default.hidden,(()=>{modal.destroy(),isModalVisible=!1}));let students=JSON.parse(result.students),studentcount=students.length,count=0,promises=[];for(const student of students){if(!isModalVisible)break;let promise=await(0,_sitsgradepush_helper.transferMarkForStudent)(assessmentmappingid,student.userid);if(!promise.success){let generalErrorMessage=promise.message;document.getElementById("error-message-modal-sync").innerHTML='"}promises.push(promise),count+=1;let progress=Math.round(count/studentcount*100);(0,_progress.updateProgressBar)(modalProgressbar,progress,!0)}await Promise.all(promises),await modal.setButtonText("cancel","Close")}updatePageIntervalId=setInterval((()=>{updateAssessments(globalCourseid)}),15e3)}(assessmentmappingid);else{(await pushMarks(this)).success&&updateAssessments(courseid)}else showErrorMessageForButton(button,"There are no marks to transfer.")})):button.disabled=!0}))}(0,courseid),function(page,courseid){document.getElementById("push-all-button").addEventListener("click",(async function(){let mabpushbuttons=document.querySelectorAll(".push-mark-button:not([disabled])[data-assessmentmappingid]"),total=mabpushbuttons.length,count=0,promises=[];mabpushbuttons.forEach((function(button){let promise=pushMarks(button).then((function(result){return result.success&&(count+=1),result})).catch((function(error){window.console.error(error)}));promises.push(promise)})),await Promise.all(promises),page.scrollTo({top:0,behavior:"instant"}),await _notification.default.addNotification({message:count+" of "+total+" push tasks have been scheduled.",type:count===total?"success":"warning"}),updateAssessments(courseid)}))}(page,courseid),updateAssessments(courseid),updatePageIntervalId=setInterval((()=>{updateAssessments(courseid)}),15e3),document.addEventListener("visibilitychange",(function(){"hidden"===document.visibilityState?clearInterval(updatePageIntervalId):(updateAssessments(courseid),updatePageIntervalId=setInterval((()=>{updateAssessments(courseid)}),15e3))}))}})); +define("local_sitsgradepush/dashboard",["exports","./sitsgradepush_helper","core/notification"],(function(_exports,_sitsgradepush_helper,_notification){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=(obj=_notification)&&obj.__esModule?obj:{default:obj};let updatePageIntervalId=null,globalCourseid=null;async function pushMarks(assessmentmappingid){try{let result=await(0,_sitsgradepush_helper.schedulePushTask)(assessmentmappingid);result.success&&updateAssessments(globalCourseid);let message="";return!result.success&&result.message&&(message=result.message),function(assessmentmappingid,message){let currentrow=document.getElementById("marks-col-field-"+assessmentmappingid).closest("tr");null!==currentrow.nextElementSibling&¤trow.nextElementSibling.classList.contains("error-message-row")&¤trow.nextElementSibling.remove();if(""!==message){let errormessagerow=document.createElement("tr");errormessagerow.setAttribute("class","error-message-row"),errormessagerow.innerHTML='",currentrow.insertAdjacentElement("afterend",errormessagerow)}}(assessmentmappingid,message),result}catch(error){return window.console.error(error),!1}}async function updateAssessments(courseid){let update=await(0,_sitsgradepush_helper.getAssessmentsUpdate)(courseid);if(update.success){let assessments=JSON.parse(update.assessments);assessments.length>0&&function(assessments){assessments.forEach((assessment=>{let marksColumnFieldId="marks-col-field-"+assessment.assessmentmappingid,marksColumnField=document.getElementById(marksColumnFieldId);if(marksColumnField){marksColumnField.setAttribute("data-markscount",assessment.markscount);let marksCount=marksColumnField.querySelector(".marks-count");marksCount&&(marksCount.innerHTML=assessment.markscount);let marksContainer=marksColumnField.querySelector(".marks-container"),taskContainer=marksColumnField.querySelector(".task-status-container");null===assessment.task?(marksColumnField.setAttribute("data-task-running",!1),taskContainer.style.display="none",marksContainer.style.display="block"):(marksColumnField.setAttribute("data-task-running",!0),marksContainer.style.display="none",taskContainer.style.display="block",(0,_sitsgradepush_helper.updateProgressBar)(taskContainer,assessment.task.progress))}}))}(assessments)}else clearInterval(updatePageIntervalId),window.console.error(update.message)}_exports.init=(courseid,moodleVersion)=>{let page;!function(){let successMessage=localStorage.getItem("successMessage");successMessage&&(_notification.default.addNotification({message:successMessage,type:"success"}),localStorage.removeItem("successMessage"))}(),globalCourseid=courseid,page=moodleVersion>"2023100900"?window:document.getElementById("page"),function(page){document.querySelectorAll(".jump-to-dropdown-item").forEach((function(item){item.addEventListener("click",(function(){let value=item.getAttribute("data-value");if(null!==value){let pagePosition=function(page){return page instanceof Window?page.scrollY:page.scrollTop}(page),selectedTable=document.getElementById(value);if(selectedTable){let offset=-100,scrollPosition=pagePosition+selectedTable.getBoundingClientRect().top+offset;page.scrollTo({top:scrollPosition,behavior:"smooth"})}}}))}))}(page),function(courseid){updateAssessments(courseid),updatePageIntervalId=setInterval((()=>{updateAssessments(courseid)}),15e3),document.addEventListener("visibilitychange",(function(){"hidden"===document.visibilityState?clearInterval(updatePageIntervalId):(updateAssessments(courseid),updatePageIntervalId=setInterval((()=>{updateAssessments(courseid)}),15e3))}))}(courseid),function(page){let confirmTransferButton=document.getElementById("js-transfer-modal-button");if(null===confirmTransferButton)return;confirmTransferButton.addEventListener("click",(async function(){let assessmentmappingid=confirmTransferButton.getAttribute("data-assessmentmappingid");null!==assessmentmappingid&&"all"!==assessmentmappingid?await pushMarks(assessmentmappingid):"all"===assessmentmappingid&&await async function(page){let assessmentmappings=Array.from(document.querySelectorAll(".marks-col-field")).filter((element=>parseInt(element.getAttribute("data-markscount"),10)>0&&"false"===element.getAttribute("data-task-running"))),total=assessmentmappings.length,count=0,promises=[];assessmentmappings.forEach((function(element){let promise=pushMarks(element.getAttribute("data-assessmentmappingid")).then((function(result){return result.success&&(count+=1),result})).catch((function(error){window.console.error(error)}));promises.push(promise)})),await Promise.all(promises),page.scrollTo({top:0,behavior:"instant"}),await _notification.default.addNotification({message:count+" of "+total+" push tasks have been scheduled.",type:count===total?"success":"warning"}),updateAssessments(globalCourseid)}(page)}))}(page)}})); //# sourceMappingURL=dashboard.min.js.map \ No newline at end of file diff --git a/amd/build/dashboard.min.js.map b/amd/build/dashboard.min.js.map index f441c1b..ff220ea 100644 --- a/amd/build/dashboard.min.js.map +++ b/amd/build/dashboard.min.js.map @@ -1 +1 @@ -{"version":3,"file":"dashboard.min.js","sources":["../src/dashboard.js"],"sourcesContent":["import {\n schedulePushTask,\n getTransferStudents,\n transferMarkForStudent,\n getAssessmentsUpdate\n} from \"./sitsgradepush_helper\";\nimport {createProgressBar, updateProgressBar, createSpinner} from \"./progress\";\nimport notification from \"core/notification\";\nimport ModalFactory from 'core/modal_factory';\nimport ModalEvents from 'core/modal_events';\n\nlet updatePageIntervalId = null; // The interval ID for updating the progress.\nlet syncThreshold = 30; // The threshold which determines whether it is a sync or async marks transfer.\nlet globalCourseid = null; // The global variable for course ID.\nlet updatePageDelay = 15000; // The delay for updating the page.\nlet async = null; // The async config.\n\n/**\n * Initialize the dashboard page.\n *\n * @param {int} courseid\n * @param {int} syncThresholdConfig\n * @param {int} asyncConfig\n * @param {string} moodleVersion\n */\nexport const init = (courseid, syncThresholdConfig, asyncConfig, moodleVersion) => {\n // If there is a saved message by successfully mapped an assessment in localStorage, display it.\n displayNotification();\n\n // Set the sync threshold from the plugin config.\n syncThreshold = syncThresholdConfig;\n\n // Set the global variable course ID.\n globalCourseid = courseid;\n\n // Set the async config.\n async = asyncConfig;\n\n let page;\n\n // Get the scrollable page element depending on the Moodle version.\n if (moodleVersion > '2023100900') {\n // Moodle 4.3 and above.\n page = window;\n } else {\n // Moodle 4.2 and below.\n page = document.getElementById(\"page\");\n }\n\n // Initialize the module delivery dropdown list.\n let tableSelector = initModuleDeliverySelector(page);\n\n // Initialize the back to top button.\n initBackToTopButton(page, tableSelector);\n\n // Initialize the change source buttons.\n initChangeSourceButtons();\n\n // Initialize the push buttons.\n initPushMarkButtons(page, courseid);\n\n // Initialize the push all button.\n initPushAllButton(page, courseid);\n\n // Update the dashboard page with the latest information.\n // E.g. progress bars, push buttons, records icons.\n updateAssessments(courseid);\n\n // Update the page every 15 seconds.\n updatePageIntervalId = setInterval(() => {\n updateAssessments(courseid);\n }, updatePageDelay);\n\n // Add event listener to stop update the page when the page is not visible. e.g. when the user switches to another tab.\n document.addEventListener(\"visibilitychange\", function() {\n if (document.visibilityState === \"hidden\") {\n clearInterval(updatePageIntervalId);\n } else {\n updateAssessments(courseid);\n updatePageIntervalId = setInterval(() => {\n updateAssessments(courseid);\n }, updatePageDelay);\n }\n });\n};\n\n/**\n * Initialize the module delivery dropdown list.\n *\n * @param {HTMLElement} page\n * @return {HTMLElement}\n */\nfunction initModuleDeliverySelector(page) {\n // Find the module delivery table selector.\n let tableSelector = document.getElementById(\"module-delivery-selector\");\n\n // Jump to the selected module delivery table when the user selects a module delivery.\n tableSelector.addEventListener(\"change\", function() {\n // Find the selected table by ID.\n let selectedTable = document.getElementById(tableSelector.value);\n\n // Get the scroll position of the page.\n let pagePosition = getPagePosition(page);\n\n // Calculate the scroll position to be 100 pixels above the table.\n if (selectedTable) {\n let offset = -100;\n let tablePosition = selectedTable.getBoundingClientRect().top;\n let scrollPosition = pagePosition + tablePosition + offset;\n\n // Scroll to the calculated position.\n page.scrollTo({\n top: scrollPosition,\n behavior: \"smooth\"\n });\n }\n });\n\n return tableSelector;\n}\n\n/**\n * Initialize the back to top button.\n *\n * @param {HTMLElement} page\n * @param {HTMLElement} tableSelector\n */\nfunction initBackToTopButton(page, tableSelector) {\n // Find the back to top button.\n let backToTopButton = document.getElementById(\"backToTopButton\");\n\n // Show the button when the user scrolls down 100 pixels from the top of the page.\n page.addEventListener(\"scroll\", function() {\n // Get the scroll position of the page.\n if (getPagePosition(page) >= 100) {\n backToTopButton.style.display = \"block\";\n } else {\n backToTopButton.style.display = \"none\";\n }\n });\n\n // Scroll to the top of the page when the button is clicked.\n backToTopButton.addEventListener(\"click\", function() {\n page.scrollTo({top: 0, behavior: \"smooth\"});\n tableSelector.selectedIndex = 0;\n });\n}\n\n/**\n * Initialize the change source buttons.\n *\n */\nfunction initChangeSourceButtons() {\n // Get all change source buttons.\n let changesourcebuttons = document.querySelectorAll(\".change-source-button:not([disabled])\");\n\n // Add event listener to each change source button.\n // When the user clicks on each change source button, redirect to the select source page.\n if (changesourcebuttons.length > 0) {\n changesourcebuttons.forEach(function(button) {\n button.addEventListener(\"click\", function() {\n // Redirect to the change source page.\n window.location.href = button.getAttribute(\"data-url\");\n });\n });\n }\n}\n\n/**\n * Initialize the push mark buttons.\n *\n * @param {HTMLElement} page\n * @param {int} courseid\n */\nfunction initPushMarkButtons(page, courseid) {\n // Get all the push buttons that are not disabled.\n let mabpushbuttons = document.querySelectorAll(\".push-mark-button\");\n\n if (mabpushbuttons.length > 0) {\n // Push grades when the user clicks on each enabled push button.\n mabpushbuttons.forEach(function(button) {\n // Find the number of students to push grades.\n let studentcount = button.getAttribute(\"data-numberofstudents\");\n let assessmentmappingid = button.getAttribute(\"data-assessmentmappingid\");\n\n // Disable the push button if there is no assessment mapping ID.\n if (assessmentmappingid === null) {\n button.disabled = true;\n return;\n }\n\n button.addEventListener(\"click\", async function() {\n if (studentcount === '0') {\n // Show an error message if there is no student to push grades.\n showErrorMessageForButton(button, 'There are no marks to transfer.');\n return;\n }\n\n // Do synchronous marks transfer if the number of marks to be transferred is less than the sync threshold.\n // Or if the async config is disabled.\n if ((studentcount > 0 && studentcount < syncThreshold) || async === '0') {\n await syncMarksTransfer(assessmentmappingid);\n } else {\n // Schedule an asynchronous marks transfer task.\n let result = await pushMarks(this);\n if (result.success) {\n // Update the page after scheduling a marks transfer task.\n updateAssessments(courseid);\n }\n }\n });\n });\n }\n}\n\n/**\n * Initialize the push all button.\n *\n * @param {HTMLElement} page\n * @param {int} courseid\n */\nfunction initPushAllButton(page, courseid) {\n // Get the push all button.\n let pushallbutton = document.getElementById(\"push-all-button\");\n\n // Push grades for all the not disabled push buttons when the user clicks on the push all button.\n pushallbutton.addEventListener(\"click\", async function() {\n // Get the updated not disabled push buttons and has assessment ID.\n let mabpushbuttons = document.querySelectorAll(\".push-mark-button:not([disabled])[data-assessmentmappingid]\");\n\n // Number of not disabled push buttons.\n let total = mabpushbuttons.length;\n let count = 0;\n\n // Create an array to hold all the Promises.\n let promises = [];\n\n // Push grades to SITS for each component grade.\n mabpushbuttons.forEach(function(button) {\n // Create a Promise for each button and push it into the array.\n let promise = pushMarks(button)\n .then(function(result) {\n if (result.success) {\n count = count + 1;\n }\n return result;\n }).catch(function(error) {\n window.console.error(error);\n });\n\n promises.push(promise);\n });\n\n // Wait for all Promises to resolve.\n await Promise.all(promises);\n\n // Scroll to the top of the page so that the user can see the notification.\n page.scrollTo({top: 0, behavior: \"instant\"});\n\n // Show the notification.\n await notification.addNotification({\n message: count + ' of ' + total + ' push tasks have been scheduled.',\n type: (count === total) ? 'success' : 'warning'\n });\n\n // Update the page information.\n updateAssessments(courseid);\n });\n}\n\n/**\n * Schedule a push task when the user clicks on a push button.\n *\n * @param {HTMLElement} button The button element.\n * @return {Promise|boolean} Promise.\n */\nasync function pushMarks(button) {\n try {\n // Get the assessment mapping ID from the button.\n let assessmentmappingid = button.getAttribute(\"data-assessmentmappingid\");\n\n // Schedule a push task.\n let result = await schedulePushTask(assessmentmappingid);\n\n // Check if the push task is successfully scheduled.\n if (result.success) {\n // Remove the tooltip (for Firefox and Safari).\n let tooltipid = button.getAttribute(\"aria-describedby\");\n if (tooltipid !== null && document.getElementById(tooltipid) !== null) {\n document.getElementById(tooltipid).remove();\n }\n } else {\n // Show an error message if the transfer task is not successfully scheduled.\n showErrorMessageForButton(button, result.message);\n }\n\n return result;\n } catch (error) {\n window.console.error(error);\n return false;\n }\n}\n\n/**\n * Update the dashboard page with the latest information.\n * e.g. progress bars, push buttons, records icons.\n *\n * @param {int} courseid\n * @return {Promise}\n */\nasync function updateAssessments(courseid) {\n // Get latest assessments information for the dashboard page.\n let update = await getAssessmentsUpdate(courseid);\n\n if (update.success) {\n // Parse the JSON string.\n let assessments = JSON.parse(update.assessments);\n\n if (assessments.length > 0) {\n // Update the all the progress bars and push buttons.\n updateTasksProgresses(assessments);\n\n // Update the records icons.\n updateIcon(assessments);\n }\n } else {\n // Stop update the page if error occurred.\n clearInterval(updatePageIntervalId);\n window.console.error(update.message);\n }\n}\n\n/**\n * Update all the progress bars and push buttons in the dashboard page.\n *\n * @param {object[]} assessments\n */\nfunction updateTasksProgresses(assessments) {\n // Filter assessments that are having task in progress.\n let assessmentsHasTasks = assessments.filter(assessment => assessment.task !== null);\n\n // The assessment mapping IDs having task in progress.\n let assessmentIds = new Set(assessmentsHasTasks.map(item => item.assessmentmappingid));\n\n // Update the progress bars.\n updateProgressBars(assessmentsHasTasks, assessmentIds);\n\n // Update the push buttons.\n updatePushButtons(assessmentIds);\n}\n\n/**\n * Update all the progress bars in the dashboard page.\n *\n * @param {object[]} assessmentsHasTasks\n * @param {Set} assessmentIds\n */\nfunction updateProgressBars(assessmentsHasTasks, assessmentIds) {\n let progressBars = document.querySelectorAll('.progress.async');\n\n // Remove the progress bars that are not in the assessmentIds.\n progressBars.forEach(progressBar => {\n if (!assessmentIds.has(progressBar.getAttribute('data-assessmentmappingid'))) {\n progressBar.remove();\n }\n });\n\n assessmentsHasTasks.forEach(assessment => {\n let progressBarId = 'progress-bar-' + assessment.task.assessmentmappingid;\n let progressBar = document.getElementById(progressBarId);\n let task = assessment.task;\n\n // If the progress bar not exists, create a new one, otherwise update the progress.\n if (!progressBar) {\n progressBar = createProgressBar(progressBarId, 'async', task.assessmentmappingid, task.progress);\n let button = document.querySelector('.push-mark-button[data-assessmentmappingid=\"' + task.assessmentmappingid + '\"]');\n button.parentNode.parentNode.insertAdjacentElement('afterend', progressBar);\n } else {\n updateProgressBar(progressBar, task.progress);\n }\n });\n}\n\n/**\n * Update all the push buttons in the dashboard page.\n *\n * @param {Set} assessmentIds The assessment mapping IDs having task in progress.\n */\nfunction updatePushButtons(assessmentIds) {\n // Find all push buttons.\n let pushButtons = document.querySelectorAll('.push-mark-button');\n\n pushButtons.forEach(function(pushButton) {\n let assessmentmappingid = pushButton.getAttribute('data-assessmentmappingid');\n\n if (assessmentIds.has(assessmentmappingid)) {\n // If the task ID is found, show spinner and disable button.\n let spinner = createSpinner('text-light', 'spinner-border-sm');\n pushButton.innerHTML = spinner.outerHTML;\n pushButton.disabled = true;\n } else if (assessmentmappingid !== null) {\n // Reset the button to the original state.\n pushButton.innerHTML = '';\n pushButton.disabled = false;\n } else {\n // No assessment mapping, disable the button.\n pushButton.disabled = true;\n }\n });\n}\n\n/**\n * Update the icons to show that there are transfer records.\n *\n * @param {object[]} assessments\n */\nfunction updateIcon(assessments) {\n let pushButtons = document.querySelectorAll('.push-mark-button');\n\n // Get assessment mappings that have transfer records.\n let assessmentsHasTransferRecords = assessments.filter(update => update.transferrecords === 1);\n\n // Extract the assessment mapping IDs.\n let assessmentIds = new Set(assessmentsHasTransferRecords.map(assessment => assessment.assessmentmappingid));\n\n // Update the icons to show that there are transfer records.\n pushButtons.forEach(function(button) {\n let assessmentmappingid = button.getAttribute('data-assessmentmappingid');\n let icon = button.parentNode.parentNode.querySelector('.records-icon');\n if (assessmentIds.has(assessmentmappingid)) {\n if (icon.classList.contains('fa-circle-info')) {\n icon.classList.replace('fa-solid', 'fa-regular');\n icon.classList.replace('fa-circle-info', 'fa-file-lines');\n }\n } else {\n if (icon.classList.contains('fa-file-lines')) {\n icon.classList.replace('fa-regular', 'fa-solid');\n icon.classList.replace('fa-file-lines', 'fa-circle-info');\n }\n }\n });\n}\n\n/**\n * Transfer marks for all the students in the assessment mapping synchronously.\n *\n * @param {int} assessmentmappingid\n * @return {Promise}\n */\nasync function syncMarksTransfer(assessmentmappingid) {\n // Stop the page update while transferring marks.\n clearInterval(updatePageIntervalId);\n\n // Get the students to transfer marks.\n let result = await getTransferStudents(assessmentmappingid);\n\n if (result.success) {\n if (result.students.length > 0) {\n let progressbar =\n createProgressBar('dashboard-progress-bar-sync', 'sync', assessmentmappingid, 0, true);\n\n // Create a modal to show the progress.\n let modal = await ModalFactory.create({\n type: ModalFactory.types.ALERT,\n title: 'Transferring Marks',\n body: '
' + progressbar.outerHTML,\n buttons: {'cancel': 'Cancel'}\n });\n\n await modal.show();\n let isModalVisible = true;\n let modalProgressbar = document.getElementById('dashboard-progress-bar-sync');\n\n // Destroy the modal when it is hidden.\n modal.getRoot().on(ModalEvents.hidden, () => {\n modal.destroy();\n isModalVisible = false;\n });\n\n let students = JSON.parse(result.students);\n\n let studentcount = students.length;\n let count = 0;\n let promises = [];\n for (const student of students) {\n // Stop the progress if the modal is closed.\n if (!isModalVisible) {\n break;\n }\n\n // Transfer mark for each student.\n let promise = await transferMarkForStudent(assessmentmappingid, student.userid);\n if (!promise.success) {\n // Get general error message.\n let generalErrorMessage = promise.message;\n let errormessage = document.getElementById('error-message-modal-sync');\n errormessage.innerHTML = '
' + generalErrorMessage + '
';\n }\n\n promises.push(promise);\n\n // Increment the count by 1 for each student.\n count = count + 1;\n\n // Calculate the progress.\n let progress = Math.round((count / studentcount) * 100);\n updateProgressBar(modalProgressbar, progress, true);\n }\n await Promise.all(promises);\n await modal.setButtonText('cancel', 'Close');\n }\n }\n\n // Resume the page update.\n updatePageIntervalId = setInterval(() => {\n updateAssessments(globalCourseid);\n }, updatePageDelay);\n}\n\n/**\n * Show an error message at the table row under the button.\n *\n * @param {HTMLElement} button\n * @param {string} message\n */\nfunction showErrorMessageForButton(button, message) {\n // Create an error message row.\n let errormessagerow = document.createElement(\"tr\");\n\n // Set the class and content of the error message row.\n errormessagerow.setAttribute(\"class\", \"error-message-row\");\n errormessagerow.innerHTML =\n '\">' +\n '
' + message + '
' +\n '';\n\n // Find the closest row to the button.\n let currentrow = button.closest(\"tr\");\n\n // Remove the existing error message row if it exists.\n if (currentrow.nextElementSibling !== null &&\n currentrow.nextElementSibling.classList.contains(\"error-message-row\")) {\n currentrow.nextElementSibling.remove();\n }\n\n // Insert the error message row after the current row.\n currentrow.insertAdjacentElement(\"afterend\", errormessagerow);\n}\n\n/**\n * Display a notification if a success message is available in localStorage.\n */\nfunction displayNotification() {\n // Retrieve the success message from localStorage.\n let successMessage = localStorage.getItem('successMessage');\n\n // Check if a success message is available.\n if (successMessage) {\n // Display the success message using a notification library or other means.\n notification.addNotification({\n message: successMessage,\n type: 'success'\n });\n\n // Remove the success message from localStorage to avoid showing it again.\n localStorage.removeItem('successMessage');\n }\n}\n\n/**\n * Get the scroll position of the page.\n *\n * @param {HTMLElement} page\n * @return {*|number}\n */\nfunction getPagePosition(page) {\n if (page instanceof Window) {\n // Get the scroll position of the page.\n return page.scrollY;\n } else {\n // Get the scroll position of the page.\n return page.scrollTop;\n }\n}\n"],"names":["updatePageIntervalId","syncThreshold","globalCourseid","async","pushMarks","button","assessmentmappingid","getAttribute","result","success","tooltipid","document","getElementById","remove","showErrorMessageForButton","message","error","window","console","updateAssessments","courseid","update","assessments","JSON","parse","length","assessmentsHasTasks","filter","assessment","task","assessmentIds","Set","map","item","querySelectorAll","forEach","progressBar","has","progressBarId","progress","querySelector","parentNode","insertAdjacentElement","updateProgressBars","pushButton","spinner","innerHTML","outerHTML","disabled","updatePushButtons","updateTasksProgresses","pushButtons","assessmentsHasTransferRecords","transferrecords","icon","classList","contains","replace","updateIcon","clearInterval","errormessagerow","createElement","setAttribute","currentrow","closest","nextElementSibling","getPagePosition","page","Window","scrollY","scrollTop","syncThresholdConfig","asyncConfig","moodleVersion","successMessage","localStorage","getItem","addNotification","type","removeItem","displayNotification","tableSelector","addEventListener","selectedTable","value","pagePosition","offset","scrollPosition","getBoundingClientRect","top","scrollTo","behavior","initModuleDeliverySelector","backToTopButton","style","display","selectedIndex","initBackToTopButton","changesourcebuttons","location","href","initChangeSourceButtons","mabpushbuttons","studentcount","students","progressbar","modal","ModalFactory","create","types","ALERT","title","body","buttons","show","isModalVisible","modalProgressbar","getRoot","on","ModalEvents","hidden","destroy","count","promises","student","promise","userid","generalErrorMessage","push","Math","round","Promise","all","setButtonText","setInterval","syncMarksTransfer","this","initPushMarkButtons","total","then","catch","notification","initPushAllButton","visibilityState"],"mappings":"sjBAWIA,qBAAuB,KACvBC,cAAgB,GAChBC,eAAiB,KAEjBC,MAAQ,oBAqQGC,UAAUC,gBAGbC,oBAAsBD,OAAOE,aAAa,4BAG1CC,aAAe,0CAAiBF,wBAGhCE,OAAOC,QAAS,KAEZC,UAAYL,OAAOE,aAAa,oBAClB,OAAdG,WAA6D,OAAvCC,SAASC,eAAeF,YAC9CC,SAASC,eAAeF,WAAWG,cAIvCC,0BAA0BT,OAAQG,OAAOO,gBAGtCP,OACT,MAAOQ,cACLC,OAAOC,QAAQF,MAAMA,QACd,kBAWAG,kBAAkBC,cAEzBC,aAAe,8CAAqBD,aAEpCC,OAAOZ,QAAS,KAEZa,YAAcC,KAAKC,MAAMH,OAAOC,aAEhCA,YAAYG,OAAS,aAmBFH,iBAEvBI,oBAAsBJ,YAAYK,QAAOC,YAAkC,OAApBA,WAAWC,OAGlEC,cAAgB,IAAIC,IAAIL,oBAAoBM,KAAIC,MAAQA,KAAK3B,iCAezCoB,oBAAqBI,eAC1BnB,SAASuB,iBAAiB,mBAGhCC,SAAQC,cACZN,cAAcO,IAAID,YAAY7B,aAAa,8BAC5C6B,YAAYvB,YAIpBa,oBAAoBS,SAAQP,iBACpBU,cAAgB,gBAAkBV,WAAWC,KAAKvB,oBAClD8B,YAAczB,SAASC,eAAe0B,eACtCT,KAAOD,WAAWC,QAGjBO,4CAKiBA,YAAaP,KAAKU,cALtB,CACdH,aAAc,+BAAkBE,cAAe,QAAST,KAAKvB,oBAAqBuB,KAAKU,UAC1E5B,SAAS6B,cAAc,+CAAiDX,KAAKvB,oBAAsB,MACzGmC,WAAWA,WAAWC,sBAAsB,WAAYN,kBA/BvEO,CAAmBjB,oBAAqBI,wBA2CjBA,eAELnB,SAASuB,iBAAiB,qBAEhCC,SAAQ,SAASS,gBACrBtC,oBAAsBsC,WAAWrC,aAAa,+BAE9CuB,cAAcO,IAAI/B,qBAAsB,KAEpCuC,SAAU,2BAAc,aAAc,qBAC1CD,WAAWE,UAAYD,QAAQE,UAC/BH,WAAWI,UAAW,OACS,OAAxB1C,qBAEPsC,WAAWE,UAAY,qCACvBF,WAAWI,UAAW,GAGtBJ,WAAWI,UAAW,KA1D9BC,CAAkBnB,eA5BVoB,CAAsB5B,sBAgGdA,iBACZ6B,YAAcxC,SAASuB,iBAAiB,qBAGxCkB,8BAAgC9B,YAAYK,QAAON,QAAqC,IAA3BA,OAAOgC,kBAGpEvB,cAAgB,IAAIC,IAAIqB,8BAA8BpB,KAAIJ,YAAcA,WAAWtB,uBAGvF6C,YAAYhB,SAAQ,SAAS9B,YACrBC,oBAAsBD,OAAOE,aAAa,4BAC1C+C,KAAOjD,OAAOoC,WAAWA,WAAWD,cAAc,iBAClDV,cAAcO,IAAI/B,qBACdgD,KAAKC,UAAUC,SAAS,oBACxBF,KAAKC,UAAUE,QAAQ,WAAY,cACnCH,KAAKC,UAAUE,QAAQ,iBAAkB,kBAGzCH,KAAKC,UAAUC,SAAS,mBACxBF,KAAKC,UAAUE,QAAQ,aAAc,YACrCH,KAAKC,UAAUE,QAAQ,gBAAiB,sBAlH5CC,CAAWpC,mBAIfqC,cAAc3D,sBACdiB,OAAOC,QAAQF,MAAMK,OAAON,kBAqM3BD,0BAA0BT,OAAQU,aAEnC6C,gBAAkBjD,SAASkD,cAAc,MAG7CD,gBAAgBE,aAAa,QAAS,qBACtCF,gBAAgBd,UACZ,iEACkD/B,QADlD,kBAKAgD,WAAa1D,OAAO2D,QAAQ,MAGM,OAAlCD,WAAWE,oBACXF,WAAWE,mBAAmBV,UAAUC,SAAS,sBACjDO,WAAWE,mBAAmBpD,SAIlCkD,WAAWrB,sBAAsB,WAAYkB,0BA6BxCM,gBAAgBC,aACjBA,gBAAgBC,OAETD,KAAKE,QAGLF,KAAKG,wBA5iBA,CAAClD,SAAUmD,oBAAqBC,YAAaC,qBAazDN,qBAogBAO,eAAiBC,aAAaC,QAAQ,kBAGtCF,uCAEaG,gBAAgB,CACzB9D,QAAS2D,eACTI,KAAM,YAIVH,aAAaI,WAAW,mBA1hB5BC,GAGA/E,cAAgBsE,oBAGhBrE,eAAiBkB,SAGjBjB,MAAQqE,YAOJL,KAFAM,cAAgB,aAETxD,OAGAN,SAASC,eAAe,YAI/BqE,uBA0C4Bd,UAE5Bc,cAAgBtE,SAASC,eAAe,mCAG5CqE,cAAcC,iBAAiB,UAAU,eAEjCC,cAAgBxE,SAASC,eAAeqE,cAAcG,OAGtDC,aAAenB,gBAAgBC,SAG/BgB,cAAe,KACXG,QAAU,IAEVC,eAAiBF,aADDF,cAAcK,wBAAwBC,IACNH,OAGpDnB,KAAKuB,SAAS,CACVD,IAAKF,eACLI,SAAU,eAKfV,cApEaW,CAA2BzB,gBA6EtBA,KAAMc,mBAE3BY,gBAAkBlF,SAASC,eAAe,mBAG9CuD,KAAKe,iBAAiB,UAAU,WAExBhB,gBAAgBC,OAAS,IACzB0B,gBAAgBC,MAAMC,QAAU,QAEhCF,gBAAgBC,MAAMC,QAAU,UAKxCF,gBAAgBX,iBAAiB,SAAS,WACtCf,KAAKuB,SAAS,CAACD,IAAK,EAAGE,SAAU,WACjCV,cAAce,cAAgB,KA3FlCC,CAAoB9B,KAAMc,8BAqGtBiB,oBAAsBvF,SAASuB,iBAAiB,yCAIhDgE,oBAAoBzE,OAAS,GAC7ByE,oBAAoB/D,SAAQ,SAAS9B,QACjCA,OAAO6E,iBAAiB,SAAS,WAE7BjE,OAAOkF,SAASC,KAAO/F,OAAOE,aAAa,kBA1GvD8F,YAsHyBlC,KAAM/C,cAE3BkF,eAAiB3F,SAASuB,iBAAiB,qBAE3CoE,eAAe7E,OAAS,GAExB6E,eAAenE,SAAQ,SAAS9B,YAExBkG,aAAelG,OAAOE,aAAa,yBACnCD,oBAAsBD,OAAOE,aAAa,4BAGlB,OAAxBD,oBAKJD,OAAO6E,iBAAiB,SAAS/E,oBACR,MAAjBoG,gBAQCA,aAAe,GAAKA,aAAetG,eAA4B,MAAVE,2BAyPzCG,qBAE7BqD,cAAc3D,0BAGVQ,aAAe,6CAAoBF,wBAEnCE,OAAOC,SACHD,OAAOgG,SAAS/E,OAAS,EAAG,KACxBgF,aACA,+BAAkB,8BAA+B,OAAQnG,oBAAqB,GAAG,GAGjFoG,YAAcC,uBAAaC,OAAO,CAClC9B,KAAM6B,uBAAaE,MAAMC,MACzBC,MAAO,qBACPC,KAAM,4CAA8CP,YAAY1D,UAChEkE,QAAS,QAAW,kBAGlBP,MAAMQ,WACRC,gBAAiB,EACjBC,iBAAmBzG,SAASC,eAAe,+BAG/C8F,MAAMW,UAAUC,GAAGC,sBAAYC,QAAQ,KACnCd,MAAMe,UACNN,gBAAiB,SAGjBX,SAAWjF,KAAKC,MAAMhB,OAAOgG,UAE7BD,aAAeC,SAAS/E,OACxBiG,MAAQ,EACRC,SAAW,OACV,MAAMC,WAAWpB,SAAU,KAEvBW,yBAKDU,cAAgB,gDAAuBvH,oBAAqBsH,QAAQE,YACnED,QAAQpH,QAAS,KAEdsH,oBAAsBF,QAAQ9G,QACfJ,SAASC,eAAe,4BAC9BkC,UAAY,iDAAmDiF,oBAAsB,SAGtGJ,SAASK,KAAKH,SAGdH,OAAgB,MAGZnF,SAAW0F,KAAKC,MAAOR,MAAQnB,aAAgB,qCACjCa,iBAAkB7E,UAAU,SAE5C4F,QAAQC,IAAIT,gBACZjB,MAAM2B,cAAc,SAAU,SAK5CrI,qBAAuBsI,aAAY,KAC/BnH,kBAAkBjB,kBArfJ,MA2LIqI,CAAkBjI,yBACrB,QAEgBF,UAAUoI,OAClB/H,SAEPU,kBAAkBC,eAbtBN,0BAA0BT,OAAQ,sCAPtCA,OAAO2C,UAAW,KAhI9ByF,CAAoBtE,EAAM/C,mBAkKH+C,KAAM/C,UAETT,SAASC,eAAe,mBAG9BsE,iBAAiB,SAAS/E,qBAEhCmG,eAAiB3F,SAASuB,iBAAiB,+DAG3CwG,MAAQpC,eAAe7E,OACvBiG,MAAQ,EAGRC,SAAW,GAGfrB,eAAenE,SAAQ,SAAS9B,YAExBwH,QAAUzH,UAAUC,QACnBsI,MAAK,SAASnI,eACPA,OAAOC,UACPiH,OAAgB,GAEblH,UACRoI,OAAM,SAAS5H,OACdC,OAAOC,QAAQF,MAAMA,UAG7B2G,SAASK,KAAKH,kBAIZM,QAAQC,IAAIT,UAGlBxD,KAAKuB,SAAS,CAACD,IAAK,EAAGE,SAAU,kBAG3BkD,sBAAahE,gBAAgB,CAC/B9D,QAAS2G,MAAQ,OAASgB,MAAQ,mCAClC5D,KAAO4C,QAAUgB,MAAS,UAAY,YAI1CvH,kBAAkBC,aA5MtB0H,CAAkB3E,KAAM/C,UAIxBD,kBAAkBC,UAGlBpB,qBAAuBsI,aAAY,KAC/BnH,kBAAkBC,YAxDJ,MA4DlBT,SAASuE,iBAAiB,oBAAoB,WACT,WAA7BvE,SAASoI,gBACTpF,cAAc3D,uBAEdmB,kBAAkBC,UAClBpB,qBAAuBsI,aAAY,KAC/BnH,kBAAkBC,YAlEZ"} \ No newline at end of file +{"version":3,"file":"dashboard.min.js","sources":["../src/dashboard.js"],"sourcesContent":["import {schedulePushTask, getAssessmentsUpdate, updateProgressBar} from \"./sitsgradepush_helper\";\nimport notification from \"core/notification\";\n\nlet updatePageIntervalId = null; // The interval ID for updating the progress.\nlet globalCourseid = null; // The global variable for course ID.\nlet updatePageDelay = 15000; // The delay for updating the page.\n\n/**\n * Initialize the dashboard page.\n *\n * @param {int} courseid\n * @param {string} moodleVersion\n */\nexport const init = (courseid, moodleVersion) => {\n // If there is a saved message by successfully mapped an assessment in localStorage, display it.\n displayNotification();\n\n // Set the global variable course ID.\n globalCourseid = courseid;\n\n let page;\n\n // Get the scrollable page element depending on the Moodle version.\n if (moodleVersion > '2023100900') {\n // Moodle 4.3 and above.\n page = window;\n } else {\n // Moodle 4.2 and below.\n page = document.getElementById(\"page\");\n }\n\n // Initialize the module delivery dropdown list.\n initModuleDeliverySelector(page);\n\n // Initialize assessment updates.\n initAssessmentUpdate(courseid);\n\n // Initialize confirmation modal.\n initConfirmationModal(page);\n\n\n};\n\n/**\n * Initialize the module delivery dropdown list.\n *\n * @param {HTMLElement} page\n */\nfunction initModuleDeliverySelector(page) {\n // Find all the dropdown items.\n let dropdownitems = document.querySelectorAll('.jump-to-dropdown-item');\n\n // Add event listener to each dropdown item.\n dropdownitems.forEach(function(item) {\n item.addEventListener('click', function() {\n let value = item.getAttribute('data-value');\n if (value !== null) {\n // Get the scroll position of the page.\n let pagePosition = getPagePosition(page);\n\n // Find the selected table by ID.\n let selectedTable = document.getElementById(value);\n\n // Calculate the scroll position to be 100 pixels above the table.\n if (selectedTable) {\n let offset = -100;\n let tablePosition = selectedTable.getBoundingClientRect().top;\n let scrollPosition = pagePosition + tablePosition + offset;\n\n // Scroll to the calculated position.\n page.scrollTo({\n top: scrollPosition,\n behavior: \"smooth\"\n });\n }\n }\n });\n });\n}\n\n/**\n * Initialize the confirmation modal.\n *\n * @param {HTMLElement} page\n */\nfunction initConfirmationModal(page) {\n // Find the confirmation modal.\n let confirmTransferButton = document.getElementById(\"js-transfer-modal-button\");\n\n // Exit if the confirmation modal is not found.\n if (confirmTransferButton === null) {\n return;\n }\n\n // Add event listener to the confirmation modal.\n confirmTransferButton.addEventListener(\"click\", async function() {\n let assessmentmappingid = confirmTransferButton.getAttribute('data-assessmentmappingid');\n if (assessmentmappingid !== null && assessmentmappingid !== 'all') {\n // Single transfer.\n await pushMarks(assessmentmappingid);\n } else if (assessmentmappingid === 'all') {\n // Bulk transfer.\n await pushAllMarks(page);\n }\n });\n}\n\n/**\n * Initialize the assessment updates.\n *\n * @param {int} courseid\n */\nfunction initAssessmentUpdate(courseid) {\n updateAssessments(courseid);\n\n // Update the page every 15 seconds.\n updatePageIntervalId = setInterval(() => {\n updateAssessments(courseid);\n }, updatePageDelay);\n\n // Add event listener to stop update the page when the page is not visible. e.g. when the user switches to another tab.\n document.addEventListener(\"visibilitychange\", function() {\n if (document.visibilityState === \"hidden\") {\n clearInterval(updatePageIntervalId);\n } else {\n updateAssessments(courseid);\n updatePageIntervalId = setInterval(() => {\n updateAssessments(courseid);\n }, updatePageDelay);\n }\n });\n}\n\n/**\n * Schedule a push task when the user clicks on a push button.\n *\n * @param {int} assessmentmappingid The button element.\n * @return {Promise|boolean} Promise.\n */\nasync function pushMarks(assessmentmappingid) {\n try {\n // Schedule a push task.\n let result = await schedulePushTask(assessmentmappingid);\n\n // Check if the push task is successfully scheduled.\n if (result.success) {\n updateAssessments(globalCourseid);\n }\n let message = '';\n if (!result.success && result.message) {\n message = result.message;\n }\n\n // Show error message if there is any.\n showTransferErrorMessage(assessmentmappingid, message);\n return result;\n } catch (error) {\n window.console.error(error);\n return false;\n }\n}\n\n/**\n *\n * @param {HTMLElement} page\n * @return {Promise}\n */\nasync function pushAllMarks(page) {\n let assessmentmappings = Array.from(document.querySelectorAll('.marks-col-field'))\n .filter(element =>\n parseInt(element.getAttribute('data-markscount'), 10) > 0 &&\n element.getAttribute('data-task-running') === 'false'\n );\n\n // Number of not disabled push buttons.\n let total = assessmentmappings.length;\n let count = 0;\n\n // Create an array to hold all the Promises.\n let promises = [];\n\n // Push grades to SITS for each component grade.\n assessmentmappings.forEach(function(element) {\n // Get the assessment mapping ID.\n let assessmentmappingid = element.getAttribute('data-assessmentmappingid');\n // Create a Promise for each button and push it into the array.\n let promise = pushMarks(assessmentmappingid)\n .then(function(result) {\n if (result.success) {\n count = count + 1;\n }\n return result;\n }).catch(function(error) {\n window.console.error(error);\n });\n\n promises.push(promise);\n });\n\n // Wait for all Promises to resolve.\n await Promise.all(promises);\n\n // Scroll to the top of the page so that the user can see the notification.\n page.scrollTo({top: 0, behavior: \"instant\"});\n\n // Show the notification.\n await notification.addNotification({\n message: count + ' of ' + total + ' push tasks have been scheduled.',\n type: (count === total) ? 'success' : 'warning'\n });\n\n // Update the page information.\n updateAssessments(globalCourseid);\n}\n\n/**\n * Update the dashboard page with the latest information.\n * e.g. progress bars, push buttons, records icons.\n *\n * @param {int} courseid\n * @return {Promise}\n */\nasync function updateAssessments(courseid) {\n // Get latest assessments information for the dashboard page.\n let update = await getAssessmentsUpdate(courseid);\n\n if (update.success) {\n // Parse the JSON string.\n let assessments = JSON.parse(update.assessments);\n\n if (assessments.length > 0) {\n updateMarksColumn(assessments);\n }\n } else {\n // Stop update the page if error occurred.\n clearInterval(updatePageIntervalId);\n window.console.error(update.message);\n }\n}\n\n/**\n * Update the marks' column for all assessments mappings.\n *\n * @param {object[]} assessments\n */\nfunction updateMarksColumn(assessments) {\n // Update assessment components which has mappings.\n assessments.forEach(assessment => {\n let marksColumnFieldId = 'marks-col-field-' + assessment.assessmentmappingid;\n let marksColumnField = document.getElementById(marksColumnFieldId);\n if (marksColumnField) {\n // Update the marks count.\n marksColumnField.setAttribute('data-markscount', assessment.markscount);\n let marksCount = marksColumnField.querySelector('.marks-count');\n if (marksCount) {\n marksCount.innerHTML = assessment.markscount;\n }\n\n let marksContainer = marksColumnField.querySelector('.marks-container');\n let taskContainer = marksColumnField.querySelector('.task-status-container');\n\n // Show marks information if no task running.\n if (assessment.task === null) {\n marksColumnField.setAttribute('data-task-running', false);\n taskContainer.style.display = 'none';\n marksContainer.style.display = 'block';\n } else {\n // Show task information if task running.\n marksColumnField.setAttribute('data-task-running', true);\n marksContainer.style.display = 'none';\n taskContainer.style.display = 'block';\n updateProgressBar(taskContainer, assessment.task.progress);\n }\n }\n });\n}\n\n/**\n * Show an error message at the table row under the button.\n *\n * @param {int} assessmentmappingid\n * @param {string} message\n */\nfunction showTransferErrorMessage(assessmentmappingid, message) {\n // Find the marks column field.\n let marksColumnField = document.getElementById('marks-col-field-' + assessmentmappingid);\n\n // Find the closest row to the button.\n let currentrow = marksColumnField.closest(\"tr\");\n\n // Remove the existing error message row if it exists.\n if (currentrow.nextElementSibling !== null &&\n currentrow.nextElementSibling.classList.contains(\"error-message-row\")) {\n currentrow.nextElementSibling.remove();\n }\n\n if (message !== '') {\n // Create an error message row.\n let errormessagerow = document.createElement(\"tr\");\n\n // Set the class and content of the error message row.\n errormessagerow.setAttribute(\"class\", \"error-message-row\");\n errormessagerow.innerHTML =\n '\">' +\n '
' + message + '
' +\n '';\n\n // Insert the error message row after the current row.\n currentrow.insertAdjacentElement(\"afterend\", errormessagerow);\n }\n}\n\n/**\n * Display a notification if a success message is available in localStorage.\n */\nfunction displayNotification() {\n // Retrieve the success message from localStorage.\n let successMessage = localStorage.getItem('successMessage');\n\n // Check if a success message is available.\n if (successMessage) {\n // Display the success message using a notification library or other means.\n notification.addNotification({\n message: successMessage,\n type: 'success'\n });\n\n // Remove the success message from localStorage to avoid showing it again.\n localStorage.removeItem('successMessage');\n }\n}\n\n/**\n * Get the scroll position of the page.\n *\n * @param {HTMLElement} page\n * @return {*|number}\n */\nfunction getPagePosition(page) {\n if (page instanceof Window) {\n // Get the scroll position of the page.\n return page.scrollY;\n } else {\n // Get the scroll position of the page.\n return page.scrollTop;\n }\n}\n"],"names":["updatePageIntervalId","globalCourseid","pushMarks","assessmentmappingid","result","success","updateAssessments","message","currentrow","document","getElementById","closest","nextElementSibling","classList","contains","remove","errormessagerow","createElement","setAttribute","innerHTML","insertAdjacentElement","showTransferErrorMessage","error","window","console","courseid","update","assessments","JSON","parse","length","forEach","assessment","marksColumnFieldId","marksColumnField","markscount","marksCount","querySelector","marksContainer","taskContainer","task","style","display","progress","updateMarksColumn","clearInterval","moodleVersion","page","successMessage","localStorage","getItem","addNotification","type","removeItem","displayNotification","querySelectorAll","item","addEventListener","value","getAttribute","pagePosition","Window","scrollY","scrollTop","getPagePosition","selectedTable","offset","scrollPosition","getBoundingClientRect","top","scrollTo","behavior","initModuleDeliverySelector","setInterval","visibilityState","initAssessmentUpdate","confirmTransferButton","async","assessmentmappings","Array","from","filter","element","parseInt","total","count","promises","promise","then","catch","push","Promise","all","notification","pushAllMarks","initConfirmationModal"],"mappings":"qTAGIA,qBAAuB,KACvBC,eAAiB,oBAuINC,UAAUC,6BAGbC,aAAe,0CAAiBD,qBAGhCC,OAAOC,SACPC,kBAAkBL,oBAElBM,QAAU,UACTH,OAAOC,SAAWD,OAAOG,UAC1BA,QAAUH,OAAOG,kBAqIKJ,oBAAqBI,aAK/CC,WAHmBC,SAASC,eAAe,mBAAqBP,qBAGlCQ,QAAQ,MAGJ,OAAlCH,WAAWI,oBACXJ,WAAWI,mBAAmBC,UAAUC,SAAS,sBACjDN,WAAWI,mBAAmBG,YAGlB,KAAZR,QAAgB,KAEZS,gBAAkBP,SAASQ,cAAc,MAG7CD,gBAAgBE,aAAa,QAAS,qBACtCF,gBAAgBG,UACZ,iEACkDZ,QADlD,cAKJC,WAAWY,sBAAsB,WAAYJ,kBA1J7CK,CAAyBlB,oBAAqBI,SACvCH,OACT,MAAOkB,cACLC,OAAOC,QAAQF,MAAMA,QACd,kBAgEAhB,kBAAkBmB,cAEzBC,aAAe,8CAAqBD,aAEpCC,OAAOrB,QAAS,KAEZsB,YAAcC,KAAKC,MAAMH,OAAOC,aAEhCA,YAAYG,OAAS,YAeNH,aAEvBA,YAAYI,SAAQC,iBACZC,mBAAqB,mBAAqBD,WAAW7B,oBACrD+B,iBAAmBzB,SAASC,eAAeuB,uBAC3CC,iBAAkB,CAElBA,iBAAiBhB,aAAa,kBAAmBc,WAAWG,gBACxDC,WAAaF,iBAAiBG,cAAc,gBAC5CD,aACAA,WAAWjB,UAAYa,WAAWG,gBAGlCG,eAAiBJ,iBAAiBG,cAAc,oBAChDE,cAAgBL,iBAAiBG,cAAc,0BAG3B,OAApBL,WAAWQ,MACXN,iBAAiBhB,aAAa,qBAAqB,GACnDqB,cAAcE,MAAMC,QAAU,OAC9BJ,eAAeG,MAAMC,QAAU,UAG/BR,iBAAiBhB,aAAa,qBAAqB,GACnDoB,eAAeG,MAAMC,QAAU,OAC/BH,cAAcE,MAAMC,QAAU,oDACZH,cAAeP,WAAWQ,KAAKG,eAxCrDC,CAAkBjB,kBAItBkB,cAAc7C,sBACduB,OAAOC,QAAQF,MAAMI,OAAOnB,uBA/NhB,CAACkB,SAAUqB,qBAOvBC,qBAySAC,eAAiBC,aAAaC,QAAQ,kBAGtCF,uCAEaG,gBAAgB,CACzB5C,QAASyC,eACTI,KAAM,YAIVH,aAAaI,WAAW,mBAzT5BC,GAGArD,eAAiBwB,SAObsB,KAFAD,cAAgB,aAETvB,OAGAd,SAASC,eAAe,iBAoBHqC,MAEZtC,SAAS8C,iBAAiB,0BAGhCxB,SAAQ,SAASyB,MAC3BA,KAAKC,iBAAiB,SAAS,eACvBC,MAAQF,KAAKG,aAAa,iBAChB,OAAVD,MAAgB,KAEZE,sBAwRKb,aACjBA,gBAAgBc,OAETd,KAAKe,QAGLf,KAAKgB,UA9ReC,CAAgBjB,MAG/BkB,cAAgBxD,SAASC,eAAegD,UAGxCO,cAAe,KACXC,QAAU,IAEVC,eAAiBP,aADDK,cAAcG,wBAAwBC,IACNH,OAGpDnB,KAAKuB,SAAS,CACVD,IAAKF,eACLI,SAAU,mBAxC9BC,CAA2BzB,eAgFDtB,UAC1BnB,kBAAkBmB,UAGlBzB,qBAAuByE,aAAY,KAC/BnE,kBAAkBmB,YAhHJ,MAoHlBhB,SAASgD,iBAAiB,oBAAoB,WACT,WAA7BhD,SAASiE,gBACT7B,cAAc7C,uBAEdM,kBAAkBmB,UAClBzB,qBAAuByE,aAAY,KAC/BnE,kBAAkBmB,YA1HZ,UA8BlBkD,CAAqBlD,mBAkDMsB,UAEvB6B,sBAAwBnE,SAASC,eAAe,+BAGtB,OAA1BkE,6BAKJA,sBAAsBnB,iBAAiB,SAASoB,qBACxC1E,oBAAsByE,sBAAsBjB,aAAa,4BACjC,OAAxBxD,qBAAwD,QAAxBA,0BAE1BD,UAAUC,qBACe,QAAxBA,0CAmES4C,UACpB+B,mBAAqBC,MAAMC,KAAKvE,SAAS8C,iBAAiB,qBACzD0B,QAAOC,SACJC,SAASD,QAAQvB,aAAa,mBAAoB,IAAM,GACV,UAA9CuB,QAAQvB,aAAa,uBAIzByB,MAAQN,mBAAmBhD,OAC3BuD,MAAQ,EAGRC,SAAW,GAGfR,mBAAmB/C,SAAQ,SAASmD,aAI5BK,QAAUrF,UAFYgF,QAAQvB,aAAa,6BAG1C6B,MAAK,SAASpF,eACPA,OAAOC,UACPgF,OAAgB,GAEbjF,UACRqF,OAAM,SAASnE,OACdC,OAAOC,QAAQF,MAAMA,UAG7BgE,SAASI,KAAKH,kBAIZI,QAAQC,IAAIN,UAGlBvC,KAAKuB,SAAS,CAACD,IAAK,EAAGE,SAAU,kBAG3BsB,sBAAa1C,gBAAgB,CAC/B5C,QAAS8E,MAAQ,OAASD,MAAQ,mCAClChC,KAAOiC,QAAUD,MAAS,UAAY,YAI1C9E,kBAAkBL,gBA9GJ6F,CAAa/C,SAhE3BgD,CAAsBhD"} \ No newline at end of file diff --git a/amd/build/progress.min.js b/amd/build/progress.min.js deleted file mode 100644 index eee13bc..0000000 --- a/amd/build/progress.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("local_sitsgradepush/progress",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.updateProgressBar=_exports.createSpinner=_exports.createProgressBar=void 0;_exports.createProgressBar=function(id,classname,assessmentmappingid,progress){let showPercentage=arguments.length>4&&void 0!==arguments[4]&&arguments[4],progressBar=document.createElement("div");return progressBar.classList.add("progress",classname),progressBar.setAttribute("id",id),progressBar.setAttribute("data-assessmentmappingid",assessmentmappingid),updateProgressBar(progressBar,progress,showPercentage),progressBar};const updateProgressBar=function(progressBar,progress){let showPercentage=arguments.length>2&&void 0!==arguments[2]&&arguments[2],progressLabel="";showPercentage&&(progressLabel=progress+"%"),progressBar.innerHTML='
'+progressLabel+"
"};_exports.updateProgressBar=updateProgressBar;_exports.createSpinner=(color,size)=>{let spinner=document.createElement("div");return spinner.setAttribute("role","status"),spinner.classList.add("spinner-border",color,size),spinner.innerHTML='Loading...',spinner}})); - -//# sourceMappingURL=progress.min.js.map \ No newline at end of file diff --git a/amd/build/progress.min.js.map b/amd/build/progress.min.js.map deleted file mode 100644 index 0508e2b..0000000 --- a/amd/build/progress.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"progress.min.js","sources":["../src/progress.js"],"sourcesContent":["/**\n * Create a progress bar.\n *\n * @param {string} id\n * @param {string} classname\n * @param {int} assessmentmappingid\n * @param {int} progress\n * @param {boolean} showPercentage\n * @return {HTMLElement}\n */\nexport const createProgressBar = (id, classname, assessmentmappingid, progress, showPercentage = false) => {\n // Create the progress bar and set the attributes.\n let progressBar = document.createElement('div');\n progressBar.classList.add('progress', classname);\n progressBar.setAttribute('id', id);\n progressBar.setAttribute('data-assessmentmappingid', assessmentmappingid);\n\n // Update the progress.\n updateProgressBar(progressBar, progress, showPercentage);\n\n return progressBar;\n};\n\n/**\n * Update the progress bar.\n *\n * @param {HTMLElement} progressBar\n * @param {int} progress\n * @param {boolean} showPercentage\n * @return {void}\n */\nexport const updateProgressBar = (progressBar, progress, showPercentage = false) => {\n // Show percentage if showPercentage is true.\n let progressLabel = '';\n if (showPercentage) {\n progressLabel = progress + '%';\n }\n\n // Update the progress bar.\n progressBar.innerHTML = '
' + progressLabel + '
';\n};\n\n/**\n * Create a spinner.\n *\n * @param {string} color\n * @param {string} size\n * @return {HTMLElement}\n */\nexport const createSpinner = (color, size) => {\n // Create the spinner and set the attributes.\n let spinner = document.createElement('div');\n spinner.setAttribute('role', 'status');\n spinner.classList.add('spinner-border', color, size);\n spinner.innerHTML = 'Loading...';\n\n return spinner;\n};\n"],"names":["id","classname","assessmentmappingid","progress","showPercentage","progressBar","document","createElement","classList","add","setAttribute","updateProgressBar","progressLabel","innerHTML","color","size","spinner"],"mappings":"6OAUiC,SAACA,GAAIC,UAAWC,oBAAqBC,cAAUC,uEAExEC,YAAcC,SAASC,cAAc,cACzCF,YAAYG,UAAUC,IAAI,WAAYR,WACtCI,YAAYK,aAAa,KAAMV,IAC/BK,YAAYK,aAAa,2BAA4BR,qBAGrDS,kBAAkBN,YAAaF,SAAUC,gBAElCC,mBAWEM,kBAAoB,SAACN,YAAaF,cAAUC,uEAEjDQ,cAAgB,GAChBR,iBACAQ,cAAgBT,SAAW,KAI/BE,YAAYQ,UAAY,+DACpBV,SAAW,wDAA0DA,SAAW,MAAQS,cAAgB,8EAUnF,CAACE,MAAOC,YAE7BC,QAAUV,SAASC,cAAc,cACrCS,QAAQN,aAAa,OAAQ,UAC7BM,QAAQR,UAAUC,IAAI,iBAAkBK,MAAOC,MAC/CC,QAAQH,UAAY,0CAEbG"} \ No newline at end of file diff --git a/amd/build/select_source.min.js b/amd/build/select_source.min.js deleted file mode 100644 index 0e9fb47..0000000 --- a/amd/build/select_source.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("local_sitsgradepush/select_source",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0;_exports.init=(courseid,mabid)=>{document.getElementById("existing-activity").addEventListener("click",(event=>{event.preventDefault(),window.location.href="/local/sitsgradepush/select_source.php?courseid=".concat(courseid,"&mabid=").concat(mabid,"&source=existing")}))}})); - -//# sourceMappingURL=select_source.min.js.map \ No newline at end of file diff --git a/amd/build/select_source.min.js.map b/amd/build/select_source.min.js.map deleted file mode 100644 index ae21252..0000000 --- a/amd/build/select_source.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"select_source.min.js","sources":["../src/select_source.js"],"sourcesContent":["export const init = (courseid, mabid) => {\n // Add an event listener to the select existing activity card.\n // When the user clicks on the card, redirect to the existing activity page.\n document.getElementById('existing-activity').addEventListener('click', (event) => {\n event.preventDefault();\n window.location.href =\n `/local/sitsgradepush/select_source.php?courseid=${courseid}&mabid=${mabid}&source=existing`;\n });\n};\n"],"names":["courseid","mabid","document","getElementById","addEventListener","event","preventDefault","window","location","href"],"mappings":"sKAAoB,CAACA,SAAUC,SAG3BC,SAASC,eAAe,qBAAqBC,iBAAiB,SAAUC,QACpEA,MAAMC,iBACNC,OAAOC,SAASC,+DACuCT,2BAAkBC"} \ No newline at end of file diff --git a/amd/build/sitsgradepush.min.js b/amd/build/sitsgradepush.min.js index ca8fff0..1b9873c 100644 --- a/amd/build/sitsgradepush.min.js +++ b/amd/build/sitsgradepush.min.js @@ -1,3 +1,3 @@ -define("local_sitsgradepush/sitsgradepush",["exports","./sitsgradepush_helper","./progress","core/notification"],(function(_exports,_sitsgradepush_helper,_progress,_notification){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=(obj=_notification)&&obj.__esModule?obj:{default:obj};let updatePageIntervalId=null;async function updateTasksInfo(courseid){let update=await(0,_sitsgradepush_helper.getAssessmentsUpdate)(courseid);if(update.success){let assessments=JSON.parse(update.assessments);assessments.length>0?(function(assessments){let taskStatusContainers=document.querySelectorAll(".task-status"),assessmentsHasTasks=assessments.filter((assessment=>null!==assessment.task)),assessmentIds=new Set(assessmentsHasTasks.map((item=>item.assessmentmappingid)));taskStatusContainers.forEach((taskStatusContainer=>{assessmentIds.has(taskStatusContainer.getAttribute("data-assessmentmappingid"))||(taskStatusContainer.innerHTML="")})),assessmentsHasTasks.forEach((assessment=>{let task=assessment.task,progressBarId="progress-bar-"+task.assessmentmappingid,progressBar=document.getElementById(progressBarId);if(progressBar)(0,_progress.updateProgressBar)(progressBar,task.progress);else{progressBar=(0,_progress.createProgressBar)(progressBarId,"async",task.assessmentmappingid,0);let taskStatusContainer=document.getElementById("task-status-container-"+task.assessmentmappingid);if(taskStatusContainer){let spinner=(0,_progress.createSpinner)("text-primary","spinner-border-sm");taskStatusContainer.appendChild(spinner),taskStatusContainer.appendChild(progressBar)}}}))}(assessments),function(assessments){document.querySelectorAll(".last-transfer-task-date").forEach((container=>{let assessment=assessments.find((assessment=>assessment.assessmentmappingid===container.getAttribute("data-assessmentmappingid")));assessment&&null!==assessment.lasttransfertime&&(container.innerHTML=assessment.lasttransfertime)}))}(assessments)):clearInterval(updatePageIntervalId)}else clearInterval(updatePageIntervalId),window.console.error(update.message)}_exports.init=(courseid,coursemoduleid,mappingids)=>{!function(courseid,mappingids){let pushbuton=document.getElementById("local_sitsgradepush_pushbutton_async");if(null===pushbuton)return;let promises=[];pushbuton.addEventListener("click",(async e=>{e.preventDefault();let total=mappingids.length,count=0;mappingids.forEach((function(mappingid){let promise=(0,_sitsgradepush_helper.schedulePushTask)(mappingid).then((function(result){if(result.success)count+=1;else{let errormessagerow=document.createElement("tr");errormessagerow.setAttribute("class","error-message-row"),errormessagerow.innerHTML='";let currentrow=document.getElementById("task-status-container-"+mappingid).closest("tr");null!==currentrow.nextElementSibling&¤trow.nextElementSibling.classList.contains("error-message-row")&¤trow.nextElementSibling.remove(),currentrow.insertAdjacentElement("afterend",errormessagerow)}return result.success})).catch((function(error){window.console.error(error)}));promises.push(promise)})),await Promise.all(promises),await updateTasksInfo(courseid),await _notification.default.addNotification({message:count+" of "+total+" push tasks have been scheduled.",type:count===total?"success":"warning"})}))}(courseid,mappingids),updateTasksInfo(courseid),updatePageIntervalId=setInterval((()=>{updateTasksInfo(courseid)}),15e3),document.addEventListener("visibilitychange",(function(){"hidden"===document.visibilityState?clearInterval(updatePageIntervalId):(updateTasksInfo(courseid),updatePageIntervalId=setInterval((()=>{updateTasksInfo(courseid)}),15e3))}))}})); +define("local_sitsgradepush/sitsgradepush",["exports","./sitsgradepush_helper","core/notification"],(function(_exports,_sitsgradepush_helper,_notification){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_notification=(obj=_notification)&&obj.__esModule?obj:{default:obj};let updatePageIntervalId=null;async function updateTasksInfo(courseid,coursemoduleid){let update=await(0,_sitsgradepush_helper.getAssessmentsUpdate)(courseid,coursemoduleid);if(update.success){let assessments=JSON.parse(update.assessments);assessments.length>0?function(assessments){assessments.forEach((assessment=>{let progressContainer=document.getElementById("progress-container-"+assessment.assessmentmappingid);if(!progressContainer)return void window.console.log("Progress container not found for assessment mapping ID: "+assessment.assessmentmappingid);let pushbutton=document.getElementById("push-all-button");null===assessment.task?(pushbutton&&(pushbutton.disabled=!1),progressContainer.style.display="none"):(pushbutton&&(pushbutton.disabled=!0),progressContainer.style.display="block",(0,_sitsgradepush_helper.updateProgressBar)(progressContainer,assessment.task.progress))}))}(assessments):clearInterval(updatePageIntervalId)}else clearInterval(updatePageIntervalId),window.console.error(update.message)}_exports.init=(courseid,coursemoduleid)=>{!function(courseid,coursemoduleid){updateTasksInfo(courseid,coursemoduleid),updatePageIntervalId=setInterval((()=>{updateTasksInfo(courseid,coursemoduleid)}),15e3),document.addEventListener("visibilitychange",(function(){"hidden"===document.visibilityState?clearInterval(updatePageIntervalId):(updateTasksInfo(courseid,coursemoduleid),updatePageIntervalId=setInterval((()=>{updateTasksInfo(courseid,coursemoduleid)}),15e3))}))}(courseid,coursemoduleid),function(courseid,coursemoduleid){let confirmTransferButton=document.getElementById("js-transfer-modal-button");if(null===confirmTransferButton)return void window.console.log("Confirmation modal not found.");confirmTransferButton.addEventListener("click",(async function(){if("1"===confirmTransferButton.getAttribute("data-async")){let promises=[],mappingtables=document.querySelectorAll(".sitsgradepush-history-table"),total=mappingtables.length-1,count=0;mappingtables.forEach((function(table){let mappingid=table.getAttribute("data-assessmentmappingid"),markscount=table.getAttribute("data-markscount");if(null!==mappingid&&markscount>0){let promise=(0,_sitsgradepush_helper.schedulePushTask)(mappingid).then((function(result){if(result.success)count+=1;else{let errormessageid="error-message-"+mappingid,errormessagerow=document.createElement("div");errormessagerow.setAttribute("id",errormessageid),errormessagerow.setAttribute("class","error-message-row"),errormessagerow.innerHTML='";let currentrow=document.getElementById(errormessageid);null!==currentrow&¤trow.remove(),table.parentNode.insertBefore(errormessagerow,table)}return result.success})).catch((function(error){window.console.error(error)}));promises.push(promise)}})),await Promise.all(promises),await updateTasksInfo(courseid,coursemoduleid),await _notification.default.addNotification({message:count+" of "+total+" push tasks have been scheduled.",type:count===total?"success":"warning"})}else window.location.href="/local/sitsgradepush/index.php?id="+coursemoduleid+"&pushgrade=1"}))}(courseid,coursemoduleid)}})); //# sourceMappingURL=sitsgradepush.min.js.map \ No newline at end of file diff --git a/amd/build/sitsgradepush.min.js.map b/amd/build/sitsgradepush.min.js.map index 9bcb659..08ee40c 100644 --- a/amd/build/sitsgradepush.min.js.map +++ b/amd/build/sitsgradepush.min.js.map @@ -1 +1 @@ -{"version":3,"file":"sitsgradepush.min.js","sources":["../src/sitsgradepush.js"],"sourcesContent":["import {schedulePushTask, getAssessmentsUpdate} from \"./sitsgradepush_helper\";\nimport {createProgressBar, updateProgressBar, createSpinner} from \"./progress\";\nimport notification from 'core/notification';\n\nlet updatePageIntervalId = null; // The interval ID for updating the progress.\nlet updatePageDelay = 15000; // The delay for updating the page.\n\n/**\n * Initialize the course module marks transfer page (index.php).\n *\n * @param {int} courseid\n * @param {int} coursemoduleid\n * @param {int[]} mappingids\n */\nexport const init = (courseid, coursemoduleid, mappingids) => {\n // Initialize the transfer marks button.\n initPushButton(courseid, mappingids);\n\n // Update the tasks progresses.\n updateTasksInfo(courseid);\n\n // Update the tasks progresses every 15 seconds.\n updatePageIntervalId = setInterval(() => {\n updateTasksInfo(courseid);\n }, updatePageDelay);\n\n // Add event listener to stop update the page when the page is not visible. e.g. when the user switches to another tab.\n document.addEventListener(\"visibilitychange\", function() {\n if (document.visibilityState === \"hidden\") {\n clearInterval(updatePageIntervalId);\n } else {\n updateTasksInfo(courseid);\n updatePageIntervalId = setInterval(() => {\n updateTasksInfo(courseid);\n }, updatePageDelay);\n }\n });\n};\n\n/**\n * Initialize the transfer marks button.\n *\n * @param {int} courseid\n * @param {int[]} mappingids\n */\nfunction initPushButton(courseid, mappingids) {\n // Get the push button.\n let pushbuton = document.getElementById('local_sitsgradepush_pushbutton_async');\n\n // Exit if the push button is not found.\n if (pushbuton === null) {\n return;\n }\n\n let promises = [];\n\n // Schedule a push task for each assessment mapping.\n pushbuton.addEventListener('click', async(e) => {\n e.preventDefault();\n\n // Number of assessment mappings.\n let total = mappingids.length;\n let count = 0;\n\n // Schedule a task to push grades to SITS for each assessment mapping.\n mappingids.forEach(function(mappingid) {\n let promise = schedulePushTask(mappingid)\n .then(function(result) {\n if (result.success) {\n count = count + 1;\n } else {\n // Create an error message row.\n let errormessagerow = document.createElement(\"tr\");\n errormessagerow.setAttribute(\"class\", \"error-message-row\");\n errormessagerow.innerHTML =\n '\">' +\n '
' + result.message + '
' +\n '';\n\n // Find the task status container.\n let taskstatus = document.getElementById('task-status-container-' + mappingid);\n\n // Find the closest row to the assessment mapping.\n let currentrow = taskstatus.closest(\"tr\");\n\n // Remove the existing error message row if it exists.\n if (currentrow.nextElementSibling !== null &&\n currentrow.nextElementSibling.classList.contains(\"error-message-row\")) {\n currentrow.nextElementSibling.remove();\n }\n\n // Insert the error message row for the assessment mapping.\n currentrow.insertAdjacentElement(\"afterend\", errormessagerow);\n }\n return result.success;\n })\n .catch(function(error) {\n window.console.error(error);\n });\n\n promises.push(promise);\n });\n\n // Wait for all the push tasks to be scheduled.\n await Promise.all(promises);\n\n await updateTasksInfo(courseid);\n\n // Display a notification.\n await notification.addNotification({\n message: count + ' of ' + total + ' push tasks have been scheduled.',\n type: (count === total) ? 'success' : 'warning'\n });\n });\n}\n\n/**\n * Update all marks transfer tasks information.\n * e.g. progress bars, spinners and last transferred task date.\n *\n * @param {int} courseid\n * @return {void}\n */\nasync function updateTasksInfo(courseid) {\n // Get all latest tasks statuses.\n let update = await getAssessmentsUpdate(courseid);\n if (update.success) {\n // Parse the JSON string.\n let assessments = JSON.parse(update.assessments);\n\n if (assessments.length > 0) {\n // Update the progress bars and spinners.\n updateProgress(assessments);\n\n // Update the last transferred task date.\n updateLastTransferredTaskDate(assessments);\n } else {\n clearInterval(updatePageIntervalId);\n }\n } else {\n // Stop updating the tasks information if there is an error getting the updated tasks information.\n clearInterval(updatePageIntervalId);\n window.console.error(update.message);\n }\n}\n\n/**\n * Update the progress bars and spinners.\n *\n * @param {object[]} assessments\n */\nfunction updateProgress(assessments) {\n // Get the task status containers.\n let taskStatusContainers = document.querySelectorAll('.task-status');\n\n // Filter assessments that are having task in progress.\n let assessmentsHasTasks = assessments.filter(assessment => assessment.task !== null);\n\n // The assessment mapping IDs having task in progress.\n let assessmentIds = new Set(assessmentsHasTasks.map(item => item.assessmentmappingid));\n\n // Remove the progress bars and spinners for the assessment mappings that are not having task in progress.\n taskStatusContainers.forEach(taskStatusContainer => {\n if (!assessmentIds.has(taskStatusContainer.getAttribute('data-assessmentmappingid'))) {\n taskStatusContainer.innerHTML = '';\n }\n });\n\n // Update the task status containers with progress bars and spinner.\n assessmentsHasTasks.forEach(assessment => {\n let task = assessment.task;\n let progressBarId = 'progress-bar-' + task.assessmentmappingid;\n let progressBar = document.getElementById(progressBarId);\n\n // If the progress bar not exists, create a new one, otherwise update the progress.\n if (!progressBar) {\n progressBar = createProgressBar(progressBarId, 'async', task.assessmentmappingid, 0);\n let taskStatusContainer = document.getElementById('task-status-container-' + task.assessmentmappingid);\n if (taskStatusContainer) {\n let spinner = createSpinner('text-primary', 'spinner-border-sm');\n taskStatusContainer.appendChild(spinner);\n taskStatusContainer.appendChild(progressBar);\n }\n } else {\n updateProgressBar(progressBar, task.progress);\n }\n });\n}\n\n/**\n * Update the last transferred task date.\n *\n * @param {object[]} assessments\n */\nfunction updateLastTransferredTaskDate(assessments) {\n let containers = document.querySelectorAll('.last-transfer-task-date');\n\n containers.forEach(container => {\n let assessment = assessments.find(\n assessment => assessment.assessmentmappingid === container.getAttribute('data-assessmentmappingid')\n );\n\n if (assessment && assessment.lasttransfertime !== null) {\n container.innerHTML = assessment.lasttransfertime;\n }\n });\n}\n"],"names":["updatePageIntervalId","updateTasksInfo","courseid","update","success","assessments","JSON","parse","length","taskStatusContainers","document","querySelectorAll","assessmentsHasTasks","filter","assessment","task","assessmentIds","Set","map","item","assessmentmappingid","forEach","taskStatusContainer","has","getAttribute","innerHTML","progressBarId","progressBar","getElementById","progress","spinner","appendChild","updateProgress","container","find","lasttransfertime","updateLastTransferredTaskDate","clearInterval","window","console","error","message","coursemoduleid","mappingids","pushbuton","promises","addEventListener","async","e","preventDefault","total","count","mappingid","promise","then","result","errormessagerow","createElement","setAttribute","currentrow","closest","nextElementSibling","classList","contains","remove","insertAdjacentElement","catch","push","Promise","all","notification","addNotification","type","initPushButton","setInterval","visibilityState"],"mappings":"gVAIIA,qBAAuB,oBAuHZC,gBAAgBC,cAEvBC,aAAe,8CAAqBD,aACpCC,OAAOC,QAAS,KAEZC,YAAcC,KAAKC,MAAMJ,OAAOE,aAEhCA,YAAYG,OAAS,YAqBTH,iBAEhBI,qBAAuBC,SAASC,iBAAiB,gBAGjDC,oBAAsBP,YAAYQ,QAAOC,YAAkC,OAApBA,WAAWC,OAGlEC,cAAgB,IAAIC,IAAIL,oBAAoBM,KAAIC,MAAQA,KAAKC,uBAGjEX,qBAAqBY,SAAQC,sBACpBN,cAAcO,IAAID,oBAAoBE,aAAa,+BACpDF,oBAAoBG,UAAY,OAKxCb,oBAAoBS,SAAQP,iBACpBC,KAAOD,WAAWC,KAClBW,cAAgB,gBAAkBX,KAAKK,oBACvCO,YAAcjB,SAASkB,eAAeF,kBAGrCC,4CASiBA,YAAaZ,KAAKc,cATtB,CACdF,aAAc,+BAAkBD,cAAe,QAASX,KAAKK,oBAAqB,OAC9EE,oBAAsBZ,SAASkB,eAAe,yBAA2Bb,KAAKK,wBAC9EE,oBAAqB,KACjBQ,SAAU,2BAAc,eAAgB,qBAC5CR,oBAAoBS,YAAYD,SAChCR,oBAAoBS,YAAYJ,kBAjDpCK,CAAe3B,sBA8DYA,aAClBK,SAASC,iBAAiB,4BAEhCU,SAAQY,gBACXnB,WAAaT,YAAY6B,MACzBpB,YAAcA,WAAWM,sBAAwBa,UAAUT,aAAa,8BAGxEV,YAA8C,OAAhCA,WAAWqB,mBACzBF,UAAUR,UAAYX,WAAWqB,qBApEjCC,CAA8B/B,cAE9BgC,cAAcrC,2BAIlBqC,cAAcrC,sBACdsC,OAAOC,QAAQC,MAAMrC,OAAOsC,uBAhIhB,CAACvC,SAAUwC,eAAgBC,wBA+BvBzC,SAAUyC,gBAE1BC,UAAYlC,SAASkB,eAAe,2CAGtB,OAAdgB,qBAIAC,SAAW,GAGfD,UAAUE,iBAAiB,SAASC,MAAAA,IAChCC,EAAEC,qBAGEC,MAAQP,WAAWnC,OACnB2C,MAAQ,EAGZR,WAAWtB,SAAQ,SAAS+B,eACpBC,SAAU,0CAAiBD,WAC1BE,MAAK,SAASC,WACPA,OAAOnD,QACP+C,OAAgB,MACb,KAECK,gBAAkB9C,SAAS+C,cAAc,MAC7CD,gBAAgBE,aAAa,QAAS,qBACtCF,gBAAgB/B,UACZ,iEACkD8B,OAAOd,QADzD,kBAQAkB,WAHajD,SAASkB,eAAe,yBAA2BwB,WAGxCQ,QAAQ,MAGE,OAAlCD,WAAWE,oBACXF,WAAWE,mBAAmBC,UAAUC,SAAS,sBACjDJ,WAAWE,mBAAmBG,SAIlCL,WAAWM,sBAAsB,WAAYT,wBAE1CD,OAAOnD,WAEjB8D,OAAM,SAAS1B,OACZF,OAAOC,QAAQC,MAAMA,UAG7BK,SAASsB,KAAKd,kBAIZe,QAAQC,IAAIxB,gBAEZ5C,gBAAgBC,gBAGhBoE,sBAAaC,gBAAgB,CAC/B9B,QAASU,MAAQ,OAASD,MAAQ,mCAClCsB,KAAOrB,QAAUD,MAAS,UAAY,eA/F9CuB,CAAevE,SAAUyC,YAGzB1C,gBAAgBC,UAGhBF,qBAAuB0E,aAAY,KAC/BzE,gBAAgBC,YAlBF,MAsBlBQ,SAASoC,iBAAiB,oBAAoB,WACT,WAA7BpC,SAASiE,gBACTtC,cAAcrC,uBAEdC,gBAAgBC,UAChBF,qBAAuB0E,aAAY,KAC/BzE,gBAAgBC,YA5BV"} \ No newline at end of file +{"version":3,"file":"sitsgradepush.min.js","sources":["../src/sitsgradepush.js"],"sourcesContent":["import {schedulePushTask, getAssessmentsUpdate, updateProgressBar} from \"./sitsgradepush_helper\";\nimport notification from 'core/notification';\n\nlet updatePageIntervalId = null; // The interval ID for updating the progress.\nlet updatePageDelay = 15000; // The delay for updating the page.\n\n/**\n * Initialize the course module marks transfer page (index.php).\n *\n * @param {int} courseid\n * @param {int} coursemoduleid\n */\nexport const init = (courseid, coursemoduleid) => {\n // Initialize page update tasks.\n initPageUpdate(courseid, coursemoduleid);\n\n // Initialize the confirmation modal.\n initConfirmationModal(courseid, coursemoduleid);\n};\n\n/**\n * Initialize page update on course module marks transfer page (index.php).\n *\n * @param {int} courseid\n * @param {int} coursemoduleid\n */\nfunction initPageUpdate(courseid, coursemoduleid) {\n // Update the tasks progresses.\n updateTasksInfo(courseid, coursemoduleid);\n\n // Update the tasks progresses every 15 seconds.\n updatePageIntervalId = setInterval(() => {\n updateTasksInfo(courseid, coursemoduleid);\n }, updatePageDelay);\n\n // Add event listener to stop update the page when the page is not visible. e.g. when the user switches to another tab.\n document.addEventListener(\"visibilitychange\", function() {\n if (document.visibilityState === \"hidden\") {\n clearInterval(updatePageIntervalId);\n } else {\n updateTasksInfo(courseid, coursemoduleid);\n updatePageIntervalId = setInterval(() => {\n updateTasksInfo(courseid, coursemoduleid);\n }, updatePageDelay);\n }\n });\n}\n\n/**\n * Initialize the confirmation modal.\n *\n * @param {int} courseid\n * @param {int} coursemoduleid\n */\nfunction initConfirmationModal(courseid, coursemoduleid) {\n // Find the confirmation modal.\n let confirmTransferButton = document.getElementById(\"js-transfer-modal-button\");\n\n // Exit if the confirmation modal is not found.\n if (confirmTransferButton === null) {\n window.console.log(\"Confirmation modal not found.\");\n return;\n }\n\n // Add event listener to the confirmation modal.\n confirmTransferButton.addEventListener(\"click\", async function() {\n // Check if it is an async push button.\n let async = confirmTransferButton.getAttribute('data-async');\n if (async === \"1\") {\n let promises = [];\n\n // Find all valid assessment mapping IDs.\n let mappingtables = document.querySelectorAll('.sitsgradepush-history-table');\n\n // Number of assessment mappings.\n let total = mappingtables.length - 1; // Exclude the invalid students table.\n let count = 0;\n\n // Schedule a task to push grades to SITS for each assessment mapping.\n mappingtables.forEach(function(table) {\n let mappingid = table.getAttribute('data-assessmentmappingid');\n let markscount = table.getAttribute('data-markscount');\n if (mappingid !== null && markscount > 0) {\n let promise = schedulePushTask(mappingid)\n .then(function(result) {\n if (result.success) {\n count = count + 1;\n } else {\n // Create an error message row.\n let errormessageid = \"error-message-\" + mappingid;\n let errormessagerow = document.createElement(\"div\");\n errormessagerow.setAttribute(\"id\", errormessageid);\n errormessagerow.setAttribute(\"class\", \"error-message-row\");\n errormessagerow.innerHTML =\n '
' + result.message + '
';\n\n // Find the closest row to the assessment mapping.\n let currentrow = document.getElementById(errormessageid);\n\n // Remove the error message row if it exists.\n if (currentrow !== null) {\n currentrow.remove();\n }\n\n // Insert the error message above the table.\n table.parentNode.insertBefore(errormessagerow, table);\n }\n return result.success;\n })\n .catch(function(error) {\n window.console.error(error);\n });\n\n promises.push(promise);\n }\n });\n\n // Wait for all the push tasks to be scheduled.\n await Promise.all(promises);\n\n // Update the page.\n await updateTasksInfo(courseid, coursemoduleid);\n\n // Display a notification.\n await notification.addNotification({\n message: count + ' of ' + total + ' push tasks have been scheduled.',\n type: (count === total) ? 'success' : 'warning'\n });\n } else {\n // Redirect to the legacy synchronous push page.\n // Will improve it when we have a more concrete plan for the sync push.\n window.location.href = '/local/sitsgradepush/index.php?id=' + coursemoduleid + '&pushgrade=1';\n }\n });\n}\n\n/**\n * Update all marks transfer tasks information, e.g. progress bars.\n *\n * @param {int} courseid\n * @param {int} coursemoduleid\n * @return {void}\n */\nasync function updateTasksInfo(courseid, coursemoduleid) {\n // Get all latest tasks statuses.\n let update = await getAssessmentsUpdate(courseid, coursemoduleid);\n if (update.success) {\n // Parse the JSON string.\n let assessments = JSON.parse(update.assessments);\n if (assessments.length > 0) {\n // Update the progress bars.\n updateProgress(assessments);\n } else {\n clearInterval(updatePageIntervalId);\n }\n } else {\n // Stop updating the tasks information if there is an error getting the updated tasks information.\n clearInterval(updatePageIntervalId);\n window.console.error(update.message);\n }\n}\n\n/**\n * Update the progress bars.\n *\n * @param {object[]} assessments\n */\nfunction updateProgress(assessments) {\n assessments.forEach(assessment => {\n let progressContainer = document.getElementById('progress-container-' + assessment.assessmentmappingid);\n if (!progressContainer) {\n window.console.log('Progress container not found for assessment mapping ID: ' + assessment.assessmentmappingid);\n return;\n }\n let pushbutton = document.getElementById('push-all-button');\n if (assessment.task === null) {\n // Enable the push button if there is no task running.\n if (pushbutton) {\n pushbutton.disabled = false;\n }\n // Hide the progress container if there is no task in progress.\n progressContainer.style.display = 'none';\n } else {\n if (pushbutton) {\n pushbutton.disabled = true;\n }\n progressContainer.style.display = 'block';\n updateProgressBar(progressContainer, assessment.task.progress);\n }\n });\n}\n"],"names":["updatePageIntervalId","updateTasksInfo","courseid","coursemoduleid","update","success","assessments","JSON","parse","length","forEach","assessment","progressContainer","document","getElementById","assessmentmappingid","window","console","log","pushbutton","task","disabled","style","display","progress","updateProgress","clearInterval","error","message","setInterval","addEventListener","visibilityState","initPageUpdate","confirmTransferButton","async","getAttribute","promises","mappingtables","querySelectorAll","total","count","table","mappingid","markscount","promise","then","result","errormessageid","errormessagerow","createElement","setAttribute","innerHTML","currentrow","remove","parentNode","insertBefore","catch","push","Promise","all","notification","addNotification","type","location","href","initConfirmationModal"],"mappings":"yTAGIA,qBAAuB,oBA4IZC,gBAAgBC,SAAUC,oBAEjCC,aAAe,8CAAqBF,SAAUC,mBAC9CC,OAAOC,QAAS,KAEZC,YAAcC,KAAKC,MAAMJ,OAAOE,aAChCA,YAAYG,OAAS,WAkBTH,aACpBA,YAAYI,SAAQC,iBACZC,kBAAoBC,SAASC,eAAe,sBAAwBH,WAAWI,yBAC9EH,8BACDI,OAAOC,QAAQC,IAAI,2DAA6DP,WAAWI,yBAG3FI,WAAaN,SAASC,eAAe,mBACjB,OAApBH,WAAWS,MAEPD,aACAA,WAAWE,UAAW,GAG1BT,kBAAkBU,MAAMC,QAAU,SAE9BJ,aACAA,WAAWE,UAAW,GAE1BT,kBAAkBU,MAAMC,QAAU,oDAChBX,kBAAmBD,WAAWS,KAAKI,cApCrDC,CAAenB,aAEfoB,cAAc1B,2BAIlB0B,cAAc1B,sBACdgB,OAAOC,QAAQU,MAAMvB,OAAOwB,uBAlJhB,CAAC1B,SAAUC,4BAcPD,SAAUC,gBAE9BF,gBAAgBC,SAAUC,gBAG1BH,qBAAuB6B,aAAY,KAC/B5B,gBAAgBC,SAAUC,kBA5BZ,MAgClBU,SAASiB,iBAAiB,oBAAoB,WACT,WAA7BjB,SAASkB,gBACTL,cAAc1B,uBAEdC,gBAAgBC,SAAUC,gBAC1BH,qBAAuB6B,aAAY,KAC/B5B,gBAAgBC,SAAUC,kBAtCpB,UAUlB6B,CAAe9B,SAAUC,yBAwCED,SAAUC,oBAEjC8B,sBAAwBpB,SAASC,eAAe,+BAGtB,OAA1BmB,kCACAjB,OAAOC,QAAQC,IAAI,iCAKvBe,sBAAsBH,iBAAiB,SAASI,oBAG9B,MADFD,sBAAsBE,aAAa,cAC5B,KACXC,SAAW,GAGXC,cAAgBxB,SAASyB,iBAAiB,gCAG1CC,MAAQF,cAAc5B,OAAS,EAC/B+B,MAAQ,EAGZH,cAAc3B,SAAQ,SAAS+B,WACvBC,UAAYD,MAAMN,aAAa,4BAC/BQ,WAAaF,MAAMN,aAAa,sBAClB,OAAdO,WAAsBC,WAAa,EAAG,KAClCC,SAAU,0CAAiBF,WAC1BG,MAAK,SAASC,WACPA,OAAOzC,QACPmC,OAAgB,MACb,KAECO,eAAiB,iBAAmBL,UACpCM,gBAAkBnC,SAASoC,cAAc,OAC7CD,gBAAgBE,aAAa,KAAMH,gBACnCC,gBAAgBE,aAAa,QAAS,qBACtCF,gBAAgBG,UACZ,gDAAkDL,OAAOlB,QAAU,aAGnEwB,WAAavC,SAASC,eAAeiC,gBAGtB,OAAfK,YACAA,WAAWC,SAIfZ,MAAMa,WAAWC,aAAaP,gBAAiBP,cAE5CK,OAAOzC,WAEjBmD,OAAM,SAAS7B,OACZX,OAAOC,QAAQU,MAAMA,UAG7BS,SAASqB,KAAKb,mBAKhBc,QAAQC,IAAIvB,gBAGZnC,gBAAgBC,SAAUC,sBAG1ByD,sBAAaC,gBAAgB,CAC/BjC,QAASY,MAAQ,OAASD,MAAQ,mCAClCuB,KAAOtB,QAAUD,MAAS,UAAY,iBAK1CvB,OAAO+C,SAASC,KAAO,qCAAuC7D,eAAiB,kBAlHvF8D,CAAsB/D,SAAUC"} \ No newline at end of file diff --git a/amd/build/sitsgradepush_helper.min.js b/amd/build/sitsgradepush_helper.min.js index c046cfa..560b26c 100644 --- a/amd/build/sitsgradepush_helper.min.js +++ b/amd/build/sitsgradepush_helper.min.js @@ -1,3 +1,3 @@ -define("local_sitsgradepush/sitsgradepush_helper",["exports","core/ajax"],(function(_exports,_ajax){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.transferMarkForStudent=_exports.schedulePushTask=_exports.mapAssessment=_exports.getTransferStudents=_exports.getAssessmentsUpdate=void 0,_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj};_exports.schedulePushTask=async assessmentmappingid=>new Promise(((resolve,reject)=>{_ajax.default.call([{methodname:"local_sitsgradepush_schedule_push_task",args:{assessmentmappingid:assessmentmappingid}}])[0].done((function(response){resolve(response)})).fail((function(err){window.console.log(err),reject(err)}))}));_exports.mapAssessment=async function(courseid,coursemoduleid,mabid){let partid=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null;return new Promise(((resolve,reject)=>{_ajax.default.call([{methodname:"local_sitsgradepush_map_assessment",args:{courseid:courseid,coursemoduleid:coursemoduleid,mabid:mabid,partid:partid}}])[0].done((function(response){resolve(response)})).fail((function(err){window.console.log(err),reject(err)}))}))};_exports.getAssessmentsUpdate=async courseid=>new Promise(((resolve,reject)=>{_ajax.default.call([{methodname:"local_sitsgradepush_get_assessments_update",args:{courseid:courseid}}])[0].done((function(response){resolve(response)})).fail((function(err){window.console.log(err),reject(err)}))}));_exports.getTransferStudents=assessmentmappingid=>new Promise(((resolve,reject)=>{_ajax.default.call([{methodname:"local_sitsgradepush_get_transfer_students",args:{assessmentmappingid:assessmentmappingid}}])[0].done((function(response){resolve(response)})).fail((function(err){window.console.log(err),reject(err)}))}));_exports.transferMarkForStudent=(assessmentmappingid,userid)=>new Promise(((resolve,reject)=>{_ajax.default.call([{methodname:"local_sitsgradepush_transfer_mark_for_student",args:{assessmentmappingid:assessmentmappingid,userid:userid}}])[0].done((function(response){resolve(response)})).fail((function(err){window.console.log(err),reject(err)}))}))})); +define("local_sitsgradepush/sitsgradepush_helper",["exports","core/ajax"],(function(_exports,_ajax){var obj;Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.updateProgressBar=_exports.schedulePushTask=_exports.mapAssessment=_exports.getAssessmentsUpdate=void 0,_ajax=(obj=_ajax)&&obj.__esModule?obj:{default:obj};_exports.schedulePushTask=async assessmentmappingid=>new Promise(((resolve,reject)=>{_ajax.default.call([{methodname:"local_sitsgradepush_schedule_push_task",args:{assessmentmappingid:assessmentmappingid}}])[0].done((function(response){resolve(response)})).fail((function(err){window.console.log(err),reject(err)}))}));_exports.mapAssessment=async function(courseid,coursemoduleid,mabid){let partid=arguments.length>3&&void 0!==arguments[3]?arguments[3]:null;return new Promise(((resolve,reject)=>{_ajax.default.call([{methodname:"local_sitsgradepush_map_assessment",args:{courseid:courseid,coursemoduleid:coursemoduleid,mabid:mabid,partid:partid}}])[0].done((function(response){resolve(response)})).fail((function(err){window.console.log(err),reject(err)}))}))};_exports.getAssessmentsUpdate=async function(courseid){let coursemoduleid=arguments.length>1&&void 0!==arguments[1]?arguments[1]:0;return new Promise(((resolve,reject)=>{_ajax.default.call([{methodname:"local_sitsgradepush_get_assessments_update",args:{courseid:courseid,coursemoduleid:coursemoduleid}}])[0].done((function(response){resolve(response)})).fail((function(err){window.console.log(err),reject(err)}))}))};_exports.updateProgressBar=(container,progress)=>{let progressLabel=container.querySelector("small"),progressBar=container.querySelector(".progress-bar");progressLabel&&progressBar&&(null===progress&&(progress=0),progressLabel.innerHTML="Progress: "+progress+"%",progressBar.setAttribute("aria-valuenow",progress),progressBar.style.width=progress+"%")}})); //# sourceMappingURL=sitsgradepush_helper.min.js.map \ No newline at end of file diff --git a/amd/build/sitsgradepush_helper.min.js.map b/amd/build/sitsgradepush_helper.min.js.map index a3be0a5..7e5abd0 100644 --- a/amd/build/sitsgradepush_helper.min.js.map +++ b/amd/build/sitsgradepush_helper.min.js.map @@ -1 +1 @@ -{"version":3,"file":"sitsgradepush_helper.min.js","sources":["../src/sitsgradepush_helper.js"],"sourcesContent":["import Ajax from 'core/ajax';\n\n/**\n * Schedule a task to push grades to SITS.\n *\n * @param {int} assessmentmappingid The assessment mapping ID.\n * @return {Promise} Promise.\n */\nexport const schedulePushTask = async(assessmentmappingid) => {\n return new Promise((resolve, reject) => {\n Ajax.call([{\n methodname: 'local_sitsgradepush_schedule_push_task',\n args: {\n 'assessmentmappingid': assessmentmappingid,\n },\n }])[0].done(function(response) {\n resolve(response);\n }).fail(function(err) {\n window.console.log(err);\n reject(err);\n });\n });\n};\n\n/**\n * Map an assessment to a component grade.\n *\n * @param {int} courseid\n * @param {int} coursemoduleid\n * @param {int} mabid\n * @param {int|null} partid\n * @return {Promise}\n */\nexport const mapAssessment = async(courseid, coursemoduleid, mabid, partid = null) => {\n return new Promise((resolve, reject) => {\n Ajax.call([{\n methodname: 'local_sitsgradepush_map_assessment',\n args: {\n 'courseid': courseid,\n 'coursemoduleid': coursemoduleid,\n 'mabid': mabid,\n 'partid': partid,\n },\n }])[0].done(function(response) {\n resolve(response);\n }).fail(function(err) {\n window.console.log(err);\n reject(err);\n });\n });\n};\n\n/**\n * Get the latest information about the assessment mappings of a course.\n * For updating the dashboard page and activity marks transfer page.\n *\n * @param {int} courseid\n * @return {Promise}\n */\nexport const getAssessmentsUpdate = async(courseid) => {\n return new Promise((resolve, reject) => {\n Ajax.call([{\n methodname: 'local_sitsgradepush_get_assessments_update',\n args: {\n 'courseid': courseid,\n },\n }])[0].done(function(response) {\n resolve(response);\n }).fail(function(err) {\n window.console.log(err);\n reject(err);\n });\n });\n};\n\n/**\n * Get the students information for a given assessment mapping.\n * For synchronous marks transfer.\n *\n * @param {int} assessmentmappingid\n * @return {Promise}\n */\nexport const getTransferStudents = (assessmentmappingid) => {\n return new Promise((resolve, reject) => {\n Ajax.call([{\n methodname: 'local_sitsgradepush_get_transfer_students',\n args: {'assessmentmappingid': assessmentmappingid},\n }])[0].done(function(response) {\n resolve(response);\n }).fail(function(err) {\n window.console.log(err);\n reject(err);\n });\n });\n};\n\n/**\n * Transfer mark for a student.\n *\n * @param {int} assessmentmappingid\n * @param {int} userid\n * @return {Promise}\n */\nexport const transferMarkForStudent = (assessmentmappingid, userid) => {\n return new Promise((resolve, reject) => {\n Ajax.call([{\n methodname: 'local_sitsgradepush_transfer_mark_for_student',\n args: {\n 'assessmentmappingid': assessmentmappingid,\n 'userid': userid\n },\n }])[0].done(function(response) {\n resolve(response);\n }).fail(function(err) {\n window.console.log(err);\n reject(err);\n });\n });\n};\n"],"names":["async","Promise","resolve","reject","call","methodname","args","assessmentmappingid","done","response","fail","err","window","console","log","courseid","coursemoduleid","mabid","partid","userid"],"mappings":"qYAQgCA,MAAAA,qBACrB,IAAIC,SAAQ,CAACC,QAASC,wBACpBC,KAAK,CAAC,CACPC,WAAY,yCACZC,KAAM,qBACqBC,wBAE3B,GAAGC,MAAK,SAASC,UACjBP,QAAQO,aACTC,MAAK,SAASC,KACbC,OAAOC,QAAQC,IAAIH,KACnBR,OAAOQ,kCAcUX,eAAMe,SAAUC,eAAgBC,WAAOC,8DAAS,YAClE,IAAIjB,SAAQ,CAACC,QAASC,wBACpBC,KAAK,CAAC,CACPC,WAAY,qCACZC,KAAM,UACUS,wBACMC,qBACTC,aACCC,WAEd,GAAGV,MAAK,SAASC,UACjBP,QAAQO,aACTC,MAAK,SAASC,KACbC,OAAOC,QAAQC,IAAIH,KACnBR,OAAOQ,0CAYiBX,MAAAA,UACzB,IAAIC,SAAQ,CAACC,QAASC,wBACpBC,KAAK,CAAC,CACPC,WAAY,6CACZC,KAAM,UACUS,aAEhB,GAAGP,MAAK,SAASC,UACjBP,QAAQO,aACTC,MAAK,SAASC,KACbC,OAAOC,QAAQC,IAAIH,KACnBR,OAAOQ,wCAYiBJ,qBACzB,IAAIN,SAAQ,CAACC,QAASC,wBACpBC,KAAK,CAAC,CACPC,WAAY,4CACZC,KAAM,qBAAwBC,wBAC9B,GAAGC,MAAK,SAASC,UACjBP,QAAQO,aACTC,MAAK,SAASC,KACbC,OAAOC,QAAQC,IAAIH,KACnBR,OAAOQ,2CAYmB,CAACJ,oBAAqBY,SACjD,IAAIlB,SAAQ,CAACC,QAASC,wBACpBC,KAAK,CAAC,CACPC,WAAY,gDACZC,KAAM,qBACqBC,2BACbY,WAEd,GAAGX,MAAK,SAASC,UACjBP,QAAQO,aACTC,MAAK,SAASC,KACbC,OAAOC,QAAQC,IAAIH,KACnBR,OAAOQ"} \ No newline at end of file +{"version":3,"file":"sitsgradepush_helper.min.js","sources":["../src/sitsgradepush_helper.js"],"sourcesContent":["import Ajax from 'core/ajax';\n\n/**\n * Schedule a task to push grades to SITS.\n *\n * @param {int} assessmentmappingid The assessment mapping ID.\n * @return {Promise} Promise.\n */\nexport const schedulePushTask = async(assessmentmappingid) => {\n return new Promise((resolve, reject) => {\n Ajax.call([{\n methodname: 'local_sitsgradepush_schedule_push_task',\n args: {\n 'assessmentmappingid': assessmentmappingid,\n },\n }])[0].done(function(response) {\n resolve(response);\n }).fail(function(err) {\n window.console.log(err);\n reject(err);\n });\n });\n};\n\n/**\n * Map an assessment to a component grade.\n *\n * @param {int} courseid\n * @param {int} coursemoduleid\n * @param {int} mabid\n * @param {int|null} partid\n * @return {Promise}\n */\nexport const mapAssessment = async(courseid, coursemoduleid, mabid, partid = null) => {\n return new Promise((resolve, reject) => {\n Ajax.call([{\n methodname: 'local_sitsgradepush_map_assessment',\n args: {\n 'courseid': courseid,\n 'coursemoduleid': coursemoduleid,\n 'mabid': mabid,\n 'partid': partid,\n },\n }])[0].done(function(response) {\n resolve(response);\n }).fail(function(err) {\n window.console.log(err);\n reject(err);\n });\n });\n};\n\n/**\n * Get the latest information about the assessment mappings of a course.\n * For updating the dashboard page and activity marks transfer page.\n *\n * @param {int} courseid\n * @param {int} coursemoduleid\n * @return {Promise}\n */\nexport const getAssessmentsUpdate = async(courseid, coursemoduleid = 0) => {\n return new Promise((resolve, reject) => {\n Ajax.call([{\n methodname: 'local_sitsgradepush_get_assessments_update',\n args: {\n 'courseid': courseid,\n 'coursemoduleid': coursemoduleid,\n },\n }])[0].done(function(response) {\n resolve(response);\n }).fail(function(err) {\n window.console.log(err);\n reject(err);\n });\n });\n};\n\n/**\n * Update the progress bar.\n *\n * @param {HTMLElement} container\n * @param {int} progress\n * @return {void}\n */\nexport const updateProgressBar = (container, progress) => {\n // Get the progress bar.\n let progressLabel = container.querySelector('small');\n let progressBar = container.querySelector('.progress-bar');\n\n if (progressLabel && progressBar) {\n if (progress === null) {\n progress = 0;\n }\n // Update the progress bar.\n progressLabel.innerHTML = 'Progress: ' + progress + '%';\n progressBar.setAttribute('aria-valuenow', progress);\n progressBar.style.width = progress + '%';\n }\n};\n"],"names":["async","Promise","resolve","reject","call","methodname","args","assessmentmappingid","done","response","fail","err","window","console","log","courseid","coursemoduleid","mabid","partid","container","progress","progressLabel","querySelector","progressBar","innerHTML","setAttribute","style","width"],"mappings":"mWAQgCA,MAAAA,qBACrB,IAAIC,SAAQ,CAACC,QAASC,wBACpBC,KAAK,CAAC,CACPC,WAAY,yCACZC,KAAM,qBACqBC,wBAE3B,GAAGC,MAAK,SAASC,UACjBP,QAAQO,aACTC,MAAK,SAASC,KACbC,OAAOC,QAAQC,IAAIH,KACnBR,OAAOQ,kCAcUX,eAAMe,SAAUC,eAAgBC,WAAOC,8DAAS,YAClE,IAAIjB,SAAQ,CAACC,QAASC,wBACpBC,KAAK,CAAC,CACPC,WAAY,qCACZC,KAAM,UACUS,wBACMC,qBACTC,aACCC,WAEd,GAAGV,MAAK,SAASC,UACjBP,QAAQO,aACTC,MAAK,SAASC,KACbC,OAAOC,QAAQC,IAAIH,KACnBR,OAAOQ,0CAaiBX,eAAMe,cAAUC,sEAAiB,SAC1D,IAAIf,SAAQ,CAACC,QAASC,wBACpBC,KAAK,CAAC,CACPC,WAAY,6CACZC,KAAM,UACUS,wBACMC,mBAEtB,GAAGR,MAAK,SAASC,UACjBP,QAAQO,aACTC,MAAK,SAASC,KACbC,OAAOC,QAAQC,IAAIH,KACnBR,OAAOQ,uCAYc,CAACQ,UAAWC,gBAErCC,cAAgBF,UAAUG,cAAc,SACxCC,YAAcJ,UAAUG,cAAc,iBAEtCD,eAAiBE,cACA,OAAbH,WACAA,SAAW,GAGfC,cAAcG,UAAY,aAAeJ,SAAW,IACpDG,YAAYE,aAAa,gBAAiBL,UAC1CG,YAAYG,MAAMC,MAAQP,SAAW"} \ No newline at end of file diff --git a/amd/src/dashboard.js b/amd/src/dashboard.js index bbca833..6306901 100644 --- a/amd/src/dashboard.js +++ b/amd/src/dashboard.js @@ -1,41 +1,23 @@ -import { - schedulePushTask, - getTransferStudents, - transferMarkForStudent, - getAssessmentsUpdate -} from "./sitsgradepush_helper"; -import {createProgressBar, updateProgressBar, createSpinner} from "./progress"; +import {schedulePushTask, getAssessmentsUpdate, updateProgressBar} from "./sitsgradepush_helper"; import notification from "core/notification"; -import ModalFactory from 'core/modal_factory'; -import ModalEvents from 'core/modal_events'; let updatePageIntervalId = null; // The interval ID for updating the progress. -let syncThreshold = 30; // The threshold which determines whether it is a sync or async marks transfer. let globalCourseid = null; // The global variable for course ID. let updatePageDelay = 15000; // The delay for updating the page. -let async = null; // The async config. /** * Initialize the dashboard page. * * @param {int} courseid - * @param {int} syncThresholdConfig - * @param {int} asyncConfig * @param {string} moodleVersion */ -export const init = (courseid, syncThresholdConfig, asyncConfig, moodleVersion) => { +export const init = (courseid, moodleVersion) => { // If there is a saved message by successfully mapped an assessment in localStorage, display it. displayNotification(); - // Set the sync threshold from the plugin config. - syncThreshold = syncThresholdConfig; - // Set the global variable course ID. globalCourseid = courseid; - // Set the async config. - async = asyncConfig; - let page; // Get the scrollable page element depending on the Moodle version. @@ -48,252 +30,129 @@ export const init = (courseid, syncThresholdConfig, asyncConfig, moodleVersion) } // Initialize the module delivery dropdown list. - let tableSelector = initModuleDeliverySelector(page); - - // Initialize the back to top button. - initBackToTopButton(page, tableSelector); + initModuleDeliverySelector(page); - // Initialize the change source buttons. - initChangeSourceButtons(); + // Initialize assessment updates. + initAssessmentUpdate(courseid); - // Initialize the push buttons. - initPushMarkButtons(page, courseid); + // Initialize confirmation modal. + initConfirmationModal(page); - // Initialize the push all button. - initPushAllButton(page, courseid); - // Update the dashboard page with the latest information. - // E.g. progress bars, push buttons, records icons. - updateAssessments(courseid); - - // Update the page every 15 seconds. - updatePageIntervalId = setInterval(() => { - updateAssessments(courseid); - }, updatePageDelay); - - // Add event listener to stop update the page when the page is not visible. e.g. when the user switches to another tab. - document.addEventListener("visibilitychange", function() { - if (document.visibilityState === "hidden") { - clearInterval(updatePageIntervalId); - } else { - updateAssessments(courseid); - updatePageIntervalId = setInterval(() => { - updateAssessments(courseid); - }, updatePageDelay); - } - }); }; /** * Initialize the module delivery dropdown list. * * @param {HTMLElement} page - * @return {HTMLElement} */ function initModuleDeliverySelector(page) { - // Find the module delivery table selector. - let tableSelector = document.getElementById("module-delivery-selector"); - - // Jump to the selected module delivery table when the user selects a module delivery. - tableSelector.addEventListener("change", function() { - // Find the selected table by ID. - let selectedTable = document.getElementById(tableSelector.value); - - // Get the scroll position of the page. - let pagePosition = getPagePosition(page); - - // Calculate the scroll position to be 100 pixels above the table. - if (selectedTable) { - let offset = -100; - let tablePosition = selectedTable.getBoundingClientRect().top; - let scrollPosition = pagePosition + tablePosition + offset; - - // Scroll to the calculated position. - page.scrollTo({ - top: scrollPosition, - behavior: "smooth" - }); - } + // Find all the dropdown items. + let dropdownitems = document.querySelectorAll('.jump-to-dropdown-item'); + + // Add event listener to each dropdown item. + dropdownitems.forEach(function(item) { + item.addEventListener('click', function() { + let value = item.getAttribute('data-value'); + if (value !== null) { + // Get the scroll position of the page. + let pagePosition = getPagePosition(page); + + // Find the selected table by ID. + let selectedTable = document.getElementById(value); + + // Calculate the scroll position to be 100 pixels above the table. + if (selectedTable) { + let offset = -100; + let tablePosition = selectedTable.getBoundingClientRect().top; + let scrollPosition = pagePosition + tablePosition + offset; + + // Scroll to the calculated position. + page.scrollTo({ + top: scrollPosition, + behavior: "smooth" + }); + } + } + }); }); - - return tableSelector; } /** - * Initialize the back to top button. + * Initialize the confirmation modal. * * @param {HTMLElement} page - * @param {HTMLElement} tableSelector */ -function initBackToTopButton(page, tableSelector) { - // Find the back to top button. - let backToTopButton = document.getElementById("backToTopButton"); - - // Show the button when the user scrolls down 100 pixels from the top of the page. - page.addEventListener("scroll", function() { - // Get the scroll position of the page. - if (getPagePosition(page) >= 100) { - backToTopButton.style.display = "block"; - } else { - backToTopButton.style.display = "none"; - } - }); - - // Scroll to the top of the page when the button is clicked. - backToTopButton.addEventListener("click", function() { - page.scrollTo({top: 0, behavior: "smooth"}); - tableSelector.selectedIndex = 0; - }); -} +function initConfirmationModal(page) { + // Find the confirmation modal. + let confirmTransferButton = document.getElementById("js-transfer-modal-button"); -/** - * Initialize the change source buttons. - * - */ -function initChangeSourceButtons() { - // Get all change source buttons. - let changesourcebuttons = document.querySelectorAll(".change-source-button:not([disabled])"); - - // Add event listener to each change source button. - // When the user clicks on each change source button, redirect to the select source page. - if (changesourcebuttons.length > 0) { - changesourcebuttons.forEach(function(button) { - button.addEventListener("click", function() { - // Redirect to the change source page. - window.location.href = button.getAttribute("data-url"); - }); - }); + // Exit if the confirmation modal is not found. + if (confirmTransferButton === null) { + return; } -} -/** - * Initialize the push mark buttons. - * - * @param {HTMLElement} page - * @param {int} courseid - */ -function initPushMarkButtons(page, courseid) { - // Get all the push buttons that are not disabled. - let mabpushbuttons = document.querySelectorAll(".push-mark-button"); - - if (mabpushbuttons.length > 0) { - // Push grades when the user clicks on each enabled push button. - mabpushbuttons.forEach(function(button) { - // Find the number of students to push grades. - let studentcount = button.getAttribute("data-numberofstudents"); - let assessmentmappingid = button.getAttribute("data-assessmentmappingid"); - - // Disable the push button if there is no assessment mapping ID. - if (assessmentmappingid === null) { - button.disabled = true; - return; - } - - button.addEventListener("click", async function() { - if (studentcount === '0') { - // Show an error message if there is no student to push grades. - showErrorMessageForButton(button, 'There are no marks to transfer.'); - return; - } - - // Do synchronous marks transfer if the number of marks to be transferred is less than the sync threshold. - // Or if the async config is disabled. - if ((studentcount > 0 && studentcount < syncThreshold) || async === '0') { - await syncMarksTransfer(assessmentmappingid); - } else { - // Schedule an asynchronous marks transfer task. - let result = await pushMarks(this); - if (result.success) { - // Update the page after scheduling a marks transfer task. - updateAssessments(courseid); - } - } - }); - }); - } + // Add event listener to the confirmation modal. + confirmTransferButton.addEventListener("click", async function() { + let assessmentmappingid = confirmTransferButton.getAttribute('data-assessmentmappingid'); + if (assessmentmappingid !== null && assessmentmappingid !== 'all') { + // Single transfer. + await pushMarks(assessmentmappingid); + } else if (assessmentmappingid === 'all') { + // Bulk transfer. + await pushAllMarks(page); + } + }); } /** - * Initialize the push all button. + * Initialize the assessment updates. * - * @param {HTMLElement} page * @param {int} courseid */ -function initPushAllButton(page, courseid) { - // Get the push all button. - let pushallbutton = document.getElementById("push-all-button"); - - // Push grades for all the not disabled push buttons when the user clicks on the push all button. - pushallbutton.addEventListener("click", async function() { - // Get the updated not disabled push buttons and has assessment ID. - let mabpushbuttons = document.querySelectorAll(".push-mark-button:not([disabled])[data-assessmentmappingid]"); - - // Number of not disabled push buttons. - let total = mabpushbuttons.length; - let count = 0; - - // Create an array to hold all the Promises. - let promises = []; - - // Push grades to SITS for each component grade. - mabpushbuttons.forEach(function(button) { - // Create a Promise for each button and push it into the array. - let promise = pushMarks(button) - .then(function(result) { - if (result.success) { - count = count + 1; - } - return result; - }).catch(function(error) { - window.console.error(error); - }); - - promises.push(promise); - }); - - // Wait for all Promises to resolve. - await Promise.all(promises); - - // Scroll to the top of the page so that the user can see the notification. - page.scrollTo({top: 0, behavior: "instant"}); - - // Show the notification. - await notification.addNotification({ - message: count + ' of ' + total + ' push tasks have been scheduled.', - type: (count === total) ? 'success' : 'warning' - }); +function initAssessmentUpdate(courseid) { + updateAssessments(courseid); - // Update the page information. + // Update the page every 15 seconds. + updatePageIntervalId = setInterval(() => { updateAssessments(courseid); + }, updatePageDelay); + + // Add event listener to stop update the page when the page is not visible. e.g. when the user switches to another tab. + document.addEventListener("visibilitychange", function() { + if (document.visibilityState === "hidden") { + clearInterval(updatePageIntervalId); + } else { + updateAssessments(courseid); + updatePageIntervalId = setInterval(() => { + updateAssessments(courseid); + }, updatePageDelay); + } }); } /** * Schedule a push task when the user clicks on a push button. * - * @param {HTMLElement} button The button element. + * @param {int} assessmentmappingid The button element. * @return {Promise|boolean} Promise. */ -async function pushMarks(button) { +async function pushMarks(assessmentmappingid) { try { - // Get the assessment mapping ID from the button. - let assessmentmappingid = button.getAttribute("data-assessmentmappingid"); - // Schedule a push task. let result = await schedulePushTask(assessmentmappingid); // Check if the push task is successfully scheduled. if (result.success) { - // Remove the tooltip (for Firefox and Safari). - let tooltipid = button.getAttribute("aria-describedby"); - if (tooltipid !== null && document.getElementById(tooltipid) !== null) { - document.getElementById(tooltipid).remove(); - } - } else { - // Show an error message if the transfer task is not successfully scheduled. - showErrorMessageForButton(button, result.message); + updateAssessments(globalCourseid); + } + let message = ''; + if (!result.success && result.message) { + message = result.message; } + // Show error message if there is any. + showTransferErrorMessage(assessmentmappingid, message); return result; } catch (error) { window.console.error(error); @@ -301,6 +160,59 @@ async function pushMarks(button) { } } +/** + * + * @param {HTMLElement} page + * @return {Promise} + */ +async function pushAllMarks(page) { + let assessmentmappings = Array.from(document.querySelectorAll('.marks-col-field')) + .filter(element => + parseInt(element.getAttribute('data-markscount'), 10) > 0 && + element.getAttribute('data-task-running') === 'false' + ); + + // Number of not disabled push buttons. + let total = assessmentmappings.length; + let count = 0; + + // Create an array to hold all the Promises. + let promises = []; + + // Push grades to SITS for each component grade. + assessmentmappings.forEach(function(element) { + // Get the assessment mapping ID. + let assessmentmappingid = element.getAttribute('data-assessmentmappingid'); + // Create a Promise for each button and push it into the array. + let promise = pushMarks(assessmentmappingid) + .then(function(result) { + if (result.success) { + count = count + 1; + } + return result; + }).catch(function(error) { + window.console.error(error); + }); + + promises.push(promise); + }); + + // Wait for all Promises to resolve. + await Promise.all(promises); + + // Scroll to the top of the page so that the user can see the notification. + page.scrollTo({top: 0, behavior: "instant"}); + + // Show the notification. + await notification.addNotification({ + message: count + ' of ' + total + ' push tasks have been scheduled.', + type: (count === total) ? 'success' : 'warning' + }); + + // Update the page information. + updateAssessments(globalCourseid); +} + /** * Update the dashboard page with the latest information. * e.g. progress bars, push buttons, records icons. @@ -317,11 +229,7 @@ async function updateAssessments(courseid) { let assessments = JSON.parse(update.assessments); if (assessments.length > 0) { - // Update the all the progress bars and push buttons. - updateTasksProgresses(assessments); - - // Update the records icons. - updateIcon(assessments); + updateMarksColumn(assessments); } } else { // Stop update the page if error occurred. @@ -331,211 +239,54 @@ async function updateAssessments(courseid) { } /** - * Update all the progress bars and push buttons in the dashboard page. + * Update the marks' column for all assessments mappings. * * @param {object[]} assessments */ -function updateTasksProgresses(assessments) { - // Filter assessments that are having task in progress. - let assessmentsHasTasks = assessments.filter(assessment => assessment.task !== null); - - // The assessment mapping IDs having task in progress. - let assessmentIds = new Set(assessmentsHasTasks.map(item => item.assessmentmappingid)); - - // Update the progress bars. - updateProgressBars(assessmentsHasTasks, assessmentIds); - - // Update the push buttons. - updatePushButtons(assessmentIds); -} - -/** - * Update all the progress bars in the dashboard page. - * - * @param {object[]} assessmentsHasTasks - * @param {Set} assessmentIds - */ -function updateProgressBars(assessmentsHasTasks, assessmentIds) { - let progressBars = document.querySelectorAll('.progress.async'); - - // Remove the progress bars that are not in the assessmentIds. - progressBars.forEach(progressBar => { - if (!assessmentIds.has(progressBar.getAttribute('data-assessmentmappingid'))) { - progressBar.remove(); - } - }); - - assessmentsHasTasks.forEach(assessment => { - let progressBarId = 'progress-bar-' + assessment.task.assessmentmappingid; - let progressBar = document.getElementById(progressBarId); - let task = assessment.task; - - // If the progress bar not exists, create a new one, otherwise update the progress. - if (!progressBar) { - progressBar = createProgressBar(progressBarId, 'async', task.assessmentmappingid, task.progress); - let button = document.querySelector('.push-mark-button[data-assessmentmappingid="' + task.assessmentmappingid + '"]'); - button.parentNode.parentNode.insertAdjacentElement('afterend', progressBar); - } else { - updateProgressBar(progressBar, task.progress); - } - }); -} - -/** - * Update all the push buttons in the dashboard page. - * - * @param {Set} assessmentIds The assessment mapping IDs having task in progress. - */ -function updatePushButtons(assessmentIds) { - // Find all push buttons. - let pushButtons = document.querySelectorAll('.push-mark-button'); - - pushButtons.forEach(function(pushButton) { - let assessmentmappingid = pushButton.getAttribute('data-assessmentmappingid'); - - if (assessmentIds.has(assessmentmappingid)) { - // If the task ID is found, show spinner and disable button. - let spinner = createSpinner('text-light', 'spinner-border-sm'); - pushButton.innerHTML = spinner.outerHTML; - pushButton.disabled = true; - } else if (assessmentmappingid !== null) { - // Reset the button to the original state. - pushButton.innerHTML = ''; - pushButton.disabled = false; - } else { - // No assessment mapping, disable the button. - pushButton.disabled = true; - } - }); -} - -/** - * Update the icons to show that there are transfer records. - * - * @param {object[]} assessments - */ -function updateIcon(assessments) { - let pushButtons = document.querySelectorAll('.push-mark-button'); - - // Get assessment mappings that have transfer records. - let assessmentsHasTransferRecords = assessments.filter(update => update.transferrecords === 1); - - // Extract the assessment mapping IDs. - let assessmentIds = new Set(assessmentsHasTransferRecords.map(assessment => assessment.assessmentmappingid)); - - // Update the icons to show that there are transfer records. - pushButtons.forEach(function(button) { - let assessmentmappingid = button.getAttribute('data-assessmentmappingid'); - let icon = button.parentNode.parentNode.querySelector('.records-icon'); - if (assessmentIds.has(assessmentmappingid)) { - if (icon.classList.contains('fa-circle-info')) { - icon.classList.replace('fa-solid', 'fa-regular'); - icon.classList.replace('fa-circle-info', 'fa-file-lines'); - } - } else { - if (icon.classList.contains('fa-file-lines')) { - icon.classList.replace('fa-regular', 'fa-solid'); - icon.classList.replace('fa-file-lines', 'fa-circle-info'); +function updateMarksColumn(assessments) { + // Update assessment components which has mappings. + assessments.forEach(assessment => { + let marksColumnFieldId = 'marks-col-field-' + assessment.assessmentmappingid; + let marksColumnField = document.getElementById(marksColumnFieldId); + if (marksColumnField) { + // Update the marks count. + marksColumnField.setAttribute('data-markscount', assessment.markscount); + let marksCount = marksColumnField.querySelector('.marks-count'); + if (marksCount) { + marksCount.innerHTML = assessment.markscount; } - } - }); -} - -/** - * Transfer marks for all the students in the assessment mapping synchronously. - * - * @param {int} assessmentmappingid - * @return {Promise} - */ -async function syncMarksTransfer(assessmentmappingid) { - // Stop the page update while transferring marks. - clearInterval(updatePageIntervalId); - - // Get the students to transfer marks. - let result = await getTransferStudents(assessmentmappingid); - - if (result.success) { - if (result.students.length > 0) { - let progressbar = - createProgressBar('dashboard-progress-bar-sync', 'sync', assessmentmappingid, 0, true); - - // Create a modal to show the progress. - let modal = await ModalFactory.create({ - type: ModalFactory.types.ALERT, - title: 'Transferring Marks', - body: '
' + progressbar.outerHTML, - buttons: {'cancel': 'Cancel'} - }); - await modal.show(); - let isModalVisible = true; - let modalProgressbar = document.getElementById('dashboard-progress-bar-sync'); - - // Destroy the modal when it is hidden. - modal.getRoot().on(ModalEvents.hidden, () => { - modal.destroy(); - isModalVisible = false; - }); - - let students = JSON.parse(result.students); - - let studentcount = students.length; - let count = 0; - let promises = []; - for (const student of students) { - // Stop the progress if the modal is closed. - if (!isModalVisible) { - break; - } - - // Transfer mark for each student. - let promise = await transferMarkForStudent(assessmentmappingid, student.userid); - if (!promise.success) { - // Get general error message. - let generalErrorMessage = promise.message; - let errormessage = document.getElementById('error-message-modal-sync'); - errormessage.innerHTML = ''; - } - - promises.push(promise); - - // Increment the count by 1 for each student. - count = count + 1; - - // Calculate the progress. - let progress = Math.round((count / studentcount) * 100); - updateProgressBar(modalProgressbar, progress, true); + let marksContainer = marksColumnField.querySelector('.marks-container'); + let taskContainer = marksColumnField.querySelector('.task-status-container'); + + // Show marks information if no task running. + if (assessment.task === null) { + marksColumnField.setAttribute('data-task-running', false); + taskContainer.style.display = 'none'; + marksContainer.style.display = 'block'; + } else { + // Show task information if task running. + marksColumnField.setAttribute('data-task-running', true); + marksContainer.style.display = 'none'; + taskContainer.style.display = 'block'; + updateProgressBar(taskContainer, assessment.task.progress); } - await Promise.all(promises); - await modal.setButtonText('cancel', 'Close'); } - } - - // Resume the page update. - updatePageIntervalId = setInterval(() => { - updateAssessments(globalCourseid); - }, updatePageDelay); + }); } /** * Show an error message at the table row under the button. * - * @param {HTMLElement} button + * @param {int} assessmentmappingid * @param {string} message */ -function showErrorMessageForButton(button, message) { - // Create an error message row. - let errormessagerow = document.createElement("tr"); - - // Set the class and content of the error message row. - errormessagerow.setAttribute("class", "error-message-row"); - errormessagerow.innerHTML = - '' + - '' + - ''; +function showTransferErrorMessage(assessmentmappingid, message) { + // Find the marks column field. + let marksColumnField = document.getElementById('marks-col-field-' + assessmentmappingid); // Find the closest row to the button. - let currentrow = button.closest("tr"); + let currentrow = marksColumnField.closest("tr"); // Remove the existing error message row if it exists. if (currentrow.nextElementSibling !== null && @@ -543,8 +294,20 @@ function showErrorMessageForButton(button, message) { currentrow.nextElementSibling.remove(); } - // Insert the error message row after the current row. - currentrow.insertAdjacentElement("afterend", errormessagerow); + if (message !== '') { + // Create an error message row. + let errormessagerow = document.createElement("tr"); + + // Set the class and content of the error message row. + errormessagerow.setAttribute("class", "error-message-row"); + errormessagerow.innerHTML = + '' + + '' + + ''; + + // Insert the error message row after the current row. + currentrow.insertAdjacentElement("afterend", errormessagerow); + } } /** diff --git a/amd/src/progress.js b/amd/src/progress.js deleted file mode 100644 index aa1b139..0000000 --- a/amd/src/progress.js +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Create a progress bar. - * - * @param {string} id - * @param {string} classname - * @param {int} assessmentmappingid - * @param {int} progress - * @param {boolean} showPercentage - * @return {HTMLElement} - */ -export const createProgressBar = (id, classname, assessmentmappingid, progress, showPercentage = false) => { - // Create the progress bar and set the attributes. - let progressBar = document.createElement('div'); - progressBar.classList.add('progress', classname); - progressBar.setAttribute('id', id); - progressBar.setAttribute('data-assessmentmappingid', assessmentmappingid); - - // Update the progress. - updateProgressBar(progressBar, progress, showPercentage); - - return progressBar; -}; - -/** - * Update the progress bar. - * - * @param {HTMLElement} progressBar - * @param {int} progress - * @param {boolean} showPercentage - * @return {void} - */ -export const updateProgressBar = (progressBar, progress, showPercentage = false) => { - // Show percentage if showPercentage is true. - let progressLabel = ''; - if (showPercentage) { - progressLabel = progress + '%'; - } - - // Update the progress bar. - progressBar.innerHTML = '
' + progressLabel + '
'; -}; - -/** - * Create a spinner. - * - * @param {string} color - * @param {string} size - * @return {HTMLElement} - */ -export const createSpinner = (color, size) => { - // Create the spinner and set the attributes. - let spinner = document.createElement('div'); - spinner.setAttribute('role', 'status'); - spinner.classList.add('spinner-border', color, size); - spinner.innerHTML = 'Loading...'; - - return spinner; -}; diff --git a/amd/src/select_source.js b/amd/src/select_source.js deleted file mode 100644 index 081b5a2..0000000 --- a/amd/src/select_source.js +++ /dev/null @@ -1,9 +0,0 @@ -export const init = (courseid, mabid) => { - // Add an event listener to the select existing activity card. - // When the user clicks on the card, redirect to the existing activity page. - document.getElementById('existing-activity').addEventListener('click', (event) => { - event.preventDefault(); - window.location.href = - `/local/sitsgradepush/select_source.php?courseid=${courseid}&mabid=${mabid}&source=existing`; - }); -}; diff --git a/amd/src/sitsgradepush.js b/amd/src/sitsgradepush.js index 0efcbef..402f747 100644 --- a/amd/src/sitsgradepush.js +++ b/amd/src/sitsgradepush.js @@ -1,5 +1,4 @@ -import {schedulePushTask, getAssessmentsUpdate} from "./sitsgradepush_helper"; -import {createProgressBar, updateProgressBar, createSpinner} from "./progress"; +import {schedulePushTask, getAssessmentsUpdate, updateProgressBar} from "./sitsgradepush_helper"; import notification from 'core/notification'; let updatePageIntervalId = null; // The interval ID for updating the progress. @@ -10,18 +9,28 @@ let updatePageDelay = 15000; // The delay for updating the page. * * @param {int} courseid * @param {int} coursemoduleid - * @param {int[]} mappingids */ -export const init = (courseid, coursemoduleid, mappingids) => { - // Initialize the transfer marks button. - initPushButton(courseid, mappingids); +export const init = (courseid, coursemoduleid) => { + // Initialize page update tasks. + initPageUpdate(courseid, coursemoduleid); + // Initialize the confirmation modal. + initConfirmationModal(courseid, coursemoduleid); +}; + +/** + * Initialize page update on course module marks transfer page (index.php). + * + * @param {int} courseid + * @param {int} coursemoduleid + */ +function initPageUpdate(courseid, coursemoduleid) { // Update the tasks progresses. - updateTasksInfo(courseid); + updateTasksInfo(courseid, coursemoduleid); // Update the tasks progresses every 15 seconds. updatePageIntervalId = setInterval(() => { - updateTasksInfo(courseid); + updateTasksInfo(courseid, coursemoduleid); }, updatePageDelay); // Add event listener to stop update the page when the page is not visible. e.g. when the user switches to another tab. @@ -29,111 +38,118 @@ export const init = (courseid, coursemoduleid, mappingids) => { if (document.visibilityState === "hidden") { clearInterval(updatePageIntervalId); } else { - updateTasksInfo(courseid); + updateTasksInfo(courseid, coursemoduleid); updatePageIntervalId = setInterval(() => { - updateTasksInfo(courseid); + updateTasksInfo(courseid, coursemoduleid); }, updatePageDelay); } }); -}; +} /** - * Initialize the transfer marks button. + * Initialize the confirmation modal. * * @param {int} courseid - * @param {int[]} mappingids + * @param {int} coursemoduleid */ -function initPushButton(courseid, mappingids) { - // Get the push button. - let pushbuton = document.getElementById('local_sitsgradepush_pushbutton_async'); +function initConfirmationModal(courseid, coursemoduleid) { + // Find the confirmation modal. + let confirmTransferButton = document.getElementById("js-transfer-modal-button"); - // Exit if the push button is not found. - if (pushbuton === null) { + // Exit if the confirmation modal is not found. + if (confirmTransferButton === null) { + window.console.log("Confirmation modal not found."); return; } - let promises = []; - - // Schedule a push task for each assessment mapping. - pushbuton.addEventListener('click', async(e) => { - e.preventDefault(); - - // Number of assessment mappings. - let total = mappingids.length; - let count = 0; - - // Schedule a task to push grades to SITS for each assessment mapping. - mappingids.forEach(function(mappingid) { - let promise = schedulePushTask(mappingid) - .then(function(result) { - if (result.success) { - count = count + 1; - } else { - // Create an error message row. - let errormessagerow = document.createElement("tr"); - errormessagerow.setAttribute("class", "error-message-row"); - errormessagerow.innerHTML = - '' + - '' + - ''; - - // Find the task status container. - let taskstatus = document.getElementById('task-status-container-' + mappingid); - - // Find the closest row to the assessment mapping. - let currentrow = taskstatus.closest("tr"); - - // Remove the existing error message row if it exists. - if (currentrow.nextElementSibling !== null && - currentrow.nextElementSibling.classList.contains("error-message-row")) { - currentrow.nextElementSibling.remove(); - } - - // Insert the error message row for the assessment mapping. - currentrow.insertAdjacentElement("afterend", errormessagerow); - } - return result.success; - }) - .catch(function(error) { - window.console.error(error); - }); - - promises.push(promise); - }); - - // Wait for all the push tasks to be scheduled. - await Promise.all(promises); - - await updateTasksInfo(courseid); - - // Display a notification. - await notification.addNotification({ - message: count + ' of ' + total + ' push tasks have been scheduled.', - type: (count === total) ? 'success' : 'warning' - }); + // Add event listener to the confirmation modal. + confirmTransferButton.addEventListener("click", async function() { + // Check if it is an async push button. + let async = confirmTransferButton.getAttribute('data-async'); + if (async === "1") { + let promises = []; + + // Find all valid assessment mapping IDs. + let mappingtables = document.querySelectorAll('.sitsgradepush-history-table'); + + // Number of assessment mappings. + let total = mappingtables.length - 1; // Exclude the invalid students table. + let count = 0; + + // Schedule a task to push grades to SITS for each assessment mapping. + mappingtables.forEach(function(table) { + let mappingid = table.getAttribute('data-assessmentmappingid'); + let markscount = table.getAttribute('data-markscount'); + if (mappingid !== null && markscount > 0) { + let promise = schedulePushTask(mappingid) + .then(function(result) { + if (result.success) { + count = count + 1; + } else { + // Create an error message row. + let errormessageid = "error-message-" + mappingid; + let errormessagerow = document.createElement("div"); + errormessagerow.setAttribute("id", errormessageid); + errormessagerow.setAttribute("class", "error-message-row"); + errormessagerow.innerHTML = + ''; + + // Find the closest row to the assessment mapping. + let currentrow = document.getElementById(errormessageid); + + // Remove the error message row if it exists. + if (currentrow !== null) { + currentrow.remove(); + } + + // Insert the error message above the table. + table.parentNode.insertBefore(errormessagerow, table); + } + return result.success; + }) + .catch(function(error) { + window.console.error(error); + }); + + promises.push(promise); + } + }); + + // Wait for all the push tasks to be scheduled. + await Promise.all(promises); + + // Update the page. + await updateTasksInfo(courseid, coursemoduleid); + + // Display a notification. + await notification.addNotification({ + message: count + ' of ' + total + ' push tasks have been scheduled.', + type: (count === total) ? 'success' : 'warning' + }); + } else { + // Redirect to the legacy synchronous push page. + // Will improve it when we have a more concrete plan for the sync push. + window.location.href = '/local/sitsgradepush/index.php?id=' + coursemoduleid + '&pushgrade=1'; + } }); } /** - * Update all marks transfer tasks information. - * e.g. progress bars, spinners and last transferred task date. + * Update all marks transfer tasks information, e.g. progress bars. * * @param {int} courseid + * @param {int} coursemoduleid * @return {void} */ -async function updateTasksInfo(courseid) { +async function updateTasksInfo(courseid, coursemoduleid) { // Get all latest tasks statuses. - let update = await getAssessmentsUpdate(courseid); + let update = await getAssessmentsUpdate(courseid, coursemoduleid); if (update.success) { // Parse the JSON string. let assessments = JSON.parse(update.assessments); - if (assessments.length > 0) { - // Update the progress bars and spinners. + // Update the progress bars. updateProgress(assessments); - - // Update the last transferred task date. - updateLastTransferredTaskDate(assessments); } else { clearInterval(updatePageIntervalId); } @@ -145,63 +161,31 @@ async function updateTasksInfo(courseid) { } /** - * Update the progress bars and spinners. + * Update the progress bars. * * @param {object[]} assessments */ function updateProgress(assessments) { - // Get the task status containers. - let taskStatusContainers = document.querySelectorAll('.task-status'); - - // Filter assessments that are having task in progress. - let assessmentsHasTasks = assessments.filter(assessment => assessment.task !== null); - - // The assessment mapping IDs having task in progress. - let assessmentIds = new Set(assessmentsHasTasks.map(item => item.assessmentmappingid)); - - // Remove the progress bars and spinners for the assessment mappings that are not having task in progress. - taskStatusContainers.forEach(taskStatusContainer => { - if (!assessmentIds.has(taskStatusContainer.getAttribute('data-assessmentmappingid'))) { - taskStatusContainer.innerHTML = ''; + assessments.forEach(assessment => { + let progressContainer = document.getElementById('progress-container-' + assessment.assessmentmappingid); + if (!progressContainer) { + window.console.log('Progress container not found for assessment mapping ID: ' + assessment.assessmentmappingid); + return; } - }); - - // Update the task status containers with progress bars and spinner. - assessmentsHasTasks.forEach(assessment => { - let task = assessment.task; - let progressBarId = 'progress-bar-' + task.assessmentmappingid; - let progressBar = document.getElementById(progressBarId); - - // If the progress bar not exists, create a new one, otherwise update the progress. - if (!progressBar) { - progressBar = createProgressBar(progressBarId, 'async', task.assessmentmappingid, 0); - let taskStatusContainer = document.getElementById('task-status-container-' + task.assessmentmappingid); - if (taskStatusContainer) { - let spinner = createSpinner('text-primary', 'spinner-border-sm'); - taskStatusContainer.appendChild(spinner); - taskStatusContainer.appendChild(progressBar); + let pushbutton = document.getElementById('push-all-button'); + if (assessment.task === null) { + // Enable the push button if there is no task running. + if (pushbutton) { + pushbutton.disabled = false; } + // Hide the progress container if there is no task in progress. + progressContainer.style.display = 'none'; } else { - updateProgressBar(progressBar, task.progress); - } - }); -} - -/** - * Update the last transferred task date. - * - * @param {object[]} assessments - */ -function updateLastTransferredTaskDate(assessments) { - let containers = document.querySelectorAll('.last-transfer-task-date'); - - containers.forEach(container => { - let assessment = assessments.find( - assessment => assessment.assessmentmappingid === container.getAttribute('data-assessmentmappingid') - ); - - if (assessment && assessment.lasttransfertime !== null) { - container.innerHTML = assessment.lasttransfertime; + if (pushbutton) { + pushbutton.disabled = true; + } + progressContainer.style.display = 'block'; + updateProgressBar(progressContainer, assessment.task.progress); } }); } diff --git a/amd/src/sitsgradepush_helper.js b/amd/src/sitsgradepush_helper.js index e00586e..75139ba 100644 --- a/amd/src/sitsgradepush_helper.js +++ b/amd/src/sitsgradepush_helper.js @@ -55,14 +55,16 @@ export const mapAssessment = async(courseid, coursemoduleid, mabid, partid = nul * For updating the dashboard page and activity marks transfer page. * * @param {int} courseid + * @param {int} coursemoduleid * @return {Promise} */ -export const getAssessmentsUpdate = async(courseid) => { +export const getAssessmentsUpdate = async(courseid, coursemoduleid = 0) => { return new Promise((resolve, reject) => { Ajax.call([{ methodname: 'local_sitsgradepush_get_assessments_update', args: { 'courseid': courseid, + 'coursemoduleid': coursemoduleid, }, }])[0].done(function(response) { resolve(response); @@ -74,46 +76,24 @@ export const getAssessmentsUpdate = async(courseid) => { }; /** - * Get the students information for a given assessment mapping. - * For synchronous marks transfer. + * Update the progress bar. * - * @param {int} assessmentmappingid - * @return {Promise} + * @param {HTMLElement} container + * @param {int} progress + * @return {void} */ -export const getTransferStudents = (assessmentmappingid) => { - return new Promise((resolve, reject) => { - Ajax.call([{ - methodname: 'local_sitsgradepush_get_transfer_students', - args: {'assessmentmappingid': assessmentmappingid}, - }])[0].done(function(response) { - resolve(response); - }).fail(function(err) { - window.console.log(err); - reject(err); - }); - }); -}; +export const updateProgressBar = (container, progress) => { + // Get the progress bar. + let progressLabel = container.querySelector('small'); + let progressBar = container.querySelector('.progress-bar'); -/** - * Transfer mark for a student. - * - * @param {int} assessmentmappingid - * @param {int} userid - * @return {Promise} - */ -export const transferMarkForStudent = (assessmentmappingid, userid) => { - return new Promise((resolve, reject) => { - Ajax.call([{ - methodname: 'local_sitsgradepush_transfer_mark_for_student', - args: { - 'assessmentmappingid': assessmentmappingid, - 'userid': userid - }, - }])[0].done(function(response) { - resolve(response); - }).fail(function(err) { - window.console.log(err); - reject(err); - }); - }); + if (progressLabel && progressBar) { + if (progress === null) { + progress = 0; + } + // Update the progress bar. + progressLabel.innerHTML = 'Progress: ' + progress + '%'; + progressBar.setAttribute('aria-valuenow', progress); + progressBar.style.width = progress + '%'; + } }; diff --git a/classes/external/get_transfer_students.php b/classes/external/get_transfer_students.php deleted file mode 100644 index 2c393e3..0000000 --- a/classes/external/get_transfer_students.php +++ /dev/null @@ -1,97 +0,0 @@ -. -namespace local_sitsgradepush\external; - -use core_external\external_api; -use core_external\external_function_parameters; -use core_external\external_single_structure; -use core_external\external_value; -use local_sitsgradepush\manager; - -/** - * External API for getting students information for an assessment mapping. - * - * @package local_sitsgradepush - * @copyright 2023 onwards University College London {@link https://www.ucl.ac.uk/} - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @author Alex Yeung - */ -class get_transfer_students extends external_api { - /** - * Returns description of method parameters. - * - * @return external_function_parameters - */ - public static function execute_parameters() { - return new external_function_parameters([ - 'assessmentmappingid' => new external_value(PARAM_INT, 'Assessment mapping ID', VALUE_REQUIRED), - ]); - } - - /** - * Returns description of method result value. - * - * @return \external_single_structure - */ - public static function execute_returns() { - return new external_single_structure([ - 'success' => new external_value(PARAM_BOOL, 'Result of request', VALUE_REQUIRED), - 'students' => new external_value(PARAM_RAW, 'Students', VALUE_OPTIONAL), - 'message' => new external_value(PARAM_TEXT, 'Error message', VALUE_OPTIONAL), - ]); - } - - /** - * Get students information for an assessment mapping - * - * @param int $assessmentmappingid - * @return array - */ - public static function execute(int $assessmentmappingid) { - try { - $params = self::validate_parameters( - self::execute_parameters(), - [ - 'assessmentmappingid' => $assessmentmappingid, - ] - ); - - $students = manager::get_manager()->get_students_in_assessment_mapping($params['assessmentmappingid']); - if (!empty($students)) { - $students = array_map(function ($student) { - return [ - 'userid' => $student->userid, - 'lastgradepushresult' => $student->lastgradepushresult, - 'lastsublogpushresult' => $student->lastsublogpushresult, - ]; - }, $students); - } else { - $students = []; - } - - return [ - 'success' => true, - 'students' => json_encode($students), - 'message' => 'Mark Transfer task completed successfully.', - ]; - } catch (\Exception $e) { - return [ - 'success' => false, - 'message' => $e->getMessage(), - ]; - } - } -} diff --git a/classes/external/transfer_mark_for_student.php b/classes/external/transfer_mark_for_student.php deleted file mode 100644 index e460194..0000000 --- a/classes/external/transfer_mark_for_student.php +++ /dev/null @@ -1,112 +0,0 @@ -. -namespace local_sitsgradepush\external; - -use core_external\external_api; -use core_external\external_function_parameters; -use core_external\external_single_structure; -use core_external\external_value; -use local_sitsgradepush\manager; - -/** - * External API for transfer mark for a student. - * - * @package local_sitsgradepush - * @copyright 2023 onwards University College London {@link https://www.ucl.ac.uk/} - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - * @author Alex Yeung - */ -class transfer_mark_for_student extends external_api { - /** - * Returns description of method parameters. - * - * @return external_function_parameters - */ - public static function execute_parameters() { - return new external_function_parameters([ - 'assessmentmappingid' => new external_value(PARAM_INT, 'Assessment mapping ID', VALUE_REQUIRED), - 'userid' => new external_value(PARAM_INT, 'User ID', VALUE_REQUIRED), - ]); - } - - /** - * Returns description of method result value. - * - * @return \external_single_structure - */ - public static function execute_returns() { - return new external_single_structure([ - 'success' => new external_value(PARAM_BOOL, 'Result of request', VALUE_REQUIRED), - 'message' => new external_value(PARAM_TEXT, 'Error message', VALUE_OPTIONAL), - ]); - } - - /** - * Transfer marks and submission logs for a student. - * - * @param int $assessmentmappingid - * @param int $userid - * - * @return array - */ - public static function execute(int $assessmentmappingid, int $userid) { - global $DB; - try { - $params = self::validate_parameters( - self::execute_parameters(), - [ - 'assessmentmappingid' => $assessmentmappingid, - 'userid' => $userid, - ] - ); - - $manager = manager::get_manager(); - - // Get assessment mapping. - $mapping = $DB->get_record(manager::TABLE_ASSESSMENT_MAPPING, ['id' => $params['assessmentmappingid']]); - - if (empty($mapping)) { - throw new \moodle_exception('error:assessmentmapping', 'local_sitsgradepush', '', $params['assessmentmappingid']); - } - - // Check if user has permission to transfer marks. - if (!has_capability('local/sitsgradepush:pushgrade', \context_course::instance($mapping->courseid))) { - throw new \moodle_exception('error:pushgradespermission', 'local_sitsgradepush'); - } - - // Get assessment mapping. - $assessmentmapping = $manager->get_assessment_mappings($mapping->coursemoduleid, $mapping->componentgradeid); - - if (!$manager->push_grade_to_sits($assessmentmapping, $params['userid'])) { - throw new \moodle_exception('error:marks_transfer_failed', 'local_sitsgradepush'); - } - - if (!$manager->push_submission_log_to_sits($assessmentmapping, $params['userid'])) { - throw new \moodle_exception('error:submission_log_transfer_failed', 'local_sitsgradepush'); - } - - return [ - 'success' => true, - 'message' => get_string('marks_transferred_successfully', 'local_sitsgradepush'), - ]; - } catch (\Exception $e) { - return [ - 'success' => false, - 'message' => $e->getMessage(), - ]; - } - } -} diff --git a/classes/manager.php b/classes/manager.php index d33b60b..7c9202e 100644 --- a/classes/manager.php +++ b/classes/manager.php @@ -89,6 +89,9 @@ class manager { /** @var string[] Allowed activity types */ const ALLOWED_ACTIVITIES = ['assign', 'quiz', 'turnitintooltwo']; + /** @var string Existing activity */ + const SOURCE_EXISTING_ACTIVITY = 'existing'; + /** @var null Manager instance */ private static $instance = null; @@ -901,12 +904,19 @@ public function get_assessment_data(int $coursemoduleid, int $assessmentmappingi $studentsfromsits[$mabkey] = array_column($this->get_students_from_sits($mapping), 'code'); $assessmentdata['mappings'][$mabkey] = $mapping; + $assessmentdata['mappings'][$mabkey]->markscount = 0; + // Students here is all the participants in that assessment. foreach ($students as $key => $student) { $studentrecord = new pushrecord($student, $coursemodule->id, $mapping); - // Add students who have push records of this mapping to the mapping's students array. + // Add participants who have push records of this mapping or + // are in the studentsfromsits array to the mapping's students array. if ($studentrecord->componentgrade == $mabkey || in_array($studentrecord->idnumber, $studentsfromsits[$mabkey])) { $assessmentdata['mappings'][$mabkey]->students[] = $studentrecord; + if ($studentrecord->marks != '-' && + !($studentrecord->isgradepushed && $studentrecord->lastgradepushresult === 'success')) { + $assessmentdata['mappings'][$mabkey]->markscount++; + } unset($students[$key]); } } @@ -1300,27 +1310,6 @@ public function get_all_course_activities(int $courseid): array { return $activities; } - /** - * Get students in an assessment mapping eligible for marks transfer. - * - * @param int $assessmentmappingid - * @return null - * @throws \coding_exception - * @throws \dml_exception - * @throws \moodle_exception - */ - public function get_students_in_assessment_mapping($assessmentmappingid) { - global $DB; - - if (!$assessmentmapping = $DB->get_record(self::TABLE_ASSESSMENT_MAPPING, ['id' => $assessmentmappingid])) { - throw new \moodle_exception('error:assessmentmapping', 'local_sitsgradepush', '', $assessmentmappingid); - } - - $mapping = $this->get_assessment_data($assessmentmapping->coursemoduleid, $assessmentmappingid); - - return !empty($mapping->students) ? $mapping->students : null; - } - /** * Get data required for page update, e.g. progress bars, last transfer time. * @@ -1330,7 +1319,7 @@ public function get_students_in_assessment_mapping($assessmentmappingid) { * @throws \coding_exception * @throws \dml_exception */ - public function get_data_for_page_update($courseid, $couresmoduleid = 0) { + public function get_data_for_page_update($courseid, $couresmoduleid = 0): array { global $DB; $results = []; @@ -1349,14 +1338,17 @@ public function get_data_for_page_update($courseid, $couresmoduleid = 0) { } foreach ($mappings as $mapping) { + // Get assessment data. + $assessmentdata = $this->get_assessment_data($mapping->coursemoduleid, $mapping->id); + // Check if there is a pending / running task for this mapping. $task = taskmanager::get_pending_task_in_queue($mapping->id); $result = new \stdClass(); $result->assessmentmappingid = $mapping->id; $result->courseid = $courseid; $result->coursemoduleid = $mapping->coursemoduleid; + $result->markscount = $assessmentdata->markscount; $result->task = !empty($task) ? $task : null; - $result->transferrecords = $this->has_grades_pushed($mapping->id) ? 1 : 0; $result->lasttransfertime = taskmanager::get_last_push_task_time($mapping->id); $results[] = $result; } diff --git a/classes/output/pushrecord.php b/classes/output/pushrecord.php index 62bfa15..83275f4 100644 --- a/classes/output/pushrecord.php +++ b/classes/output/pushrecord.php @@ -74,6 +74,12 @@ class pushrecord { /** @var string Last submission log push time */ public string $lastsublogpushtime = '-'; + /** @var bool Is grade pushed */ + public bool $isgradepushed = false; + + /** @var bool Is submission log pushed */ + public bool $issublogpushed = false; + /** @var manager|null Grade push manager */ protected ?manager $manager; @@ -103,6 +109,12 @@ public function __construct(\stdClass $student, int $coursemoduleid, \stdClass $ // Set transfer records. $this->set_transfer_records($mapping->id, $student->id); } + + // Set is pushed. + $this->set_is_grade_pushed(); + + // Set is submission log pushed. + $this->set_is_submission_log_pushed(); } /** @@ -187,4 +199,26 @@ protected function set_transfer_records(int $assessmentmappingid, int $studentid } } } + + /** + * Set is grade pushed. + * + * @return void + */ + protected function set_is_grade_pushed(): void { + if (!is_null($this->lastgradepushresult) && $this->lastgradepusherrortype == 0) { + $this->isgradepushed = true; + } + } + + /** + * Set is submission log pushed. + * + * @return void + */ + protected function set_is_submission_log_pushed(): void { + if (!is_null($this->lastsublogpushresult) && $this->lastsublogpusherrortype == 0) { + $this->issublogpushed = true; + } + } } diff --git a/classes/output/renderer.php b/classes/output/renderer.php index c087fd1..21d6b2d 100644 --- a/classes/output/renderer.php +++ b/classes/output/renderer.php @@ -83,55 +83,57 @@ public function render_link(string $id, string $name, string $url) : string { } /** - * Render the assessment push status table. + * Render the marks transfer history table. * - * @param \stdClass $mapping Assessment mapping + * @param array $assessmentdata Assessment data * @param int $courseid Course ID * @return string Rendered HTML * @throws \moodle_exception */ - public function render_assessment_push_status_table(\stdClass $mapping, int $courseid) : string { - $students = null; - // Modify the timestamp format and add the label for the last push result. - if (!empty($mapping->students)) { - foreach ($mapping->students as &$data) { - // Remove the T character in the timestamp. - $data->handindatetime = str_replace('T', ' ', $data->handindatetime); - // Add the label for the last push result. - $data->lastgradepushresultlabel = - is_null($data->lastgradepushresult) ? '' : $this->get_label_html($data->lastgradepusherrortype); - // Add the label for the last submission log push result. - $data->lastsublogpushresultlabel = - is_null($data->lastsublogpushresult) ? '' : $this->get_label_html($data->lastsublogpusherrortype); - } - $students = $mapping->students; - } + public function render_marks_transfer_history_page(array $assessmentdata, int $courseid) : string { + // Check if the user has the capability to see the submission log column. + $showsublogcolumn = has_capability('local/sitsgradepush:showsubmissionlogcolumn', \context_course::instance($courseid)); - $lasttasktext = null; - $hastaskinprogress = null; - $mappingid = null; + $mappingtables = []; + foreach ($assessmentdata['mappings'] as $mapping) { + $students = null; + // Modify the timestamp format and add the label for the last push result. + if (!empty($mapping->students)) { + foreach ($mapping->students as &$data) { + // Remove the T character in the timestamp. + $data->handindatetime = str_replace('T', ' ', $data->handindatetime); + // Add the label for the last push result. + $data->lastgradepushresultlabel = + is_null($data->lastgradepushresult) ? '' : $this->get_label_html($data->lastgradepusherrortype); + // Add the label for the last submission log push result. + $data->lastsublogpushresultlabel = + is_null($data->lastsublogpushresult) ? '' : $this->get_label_html($data->lastsublogpusherrortype); + } + $students = $mapping->students; + } - // Add last task details and push task status to the mapping object if any. - if (!empty($mapping->id)) { - $mappingid = $mapping->id; - $lasttasktext = $this->get_last_push_task_time($mapping->id); - $hastaskinprogress = taskmanager::get_pending_task_in_queue($mapping->id); + $mappingtable = new \stdClass(); + $mappingtable->mappingid = $mapping->id; + $mappingtable->markscount = $mapping->markscount ?? 0; + $mappingtable->tabletitle = $mapping->formattedname; + $mappingtable->students = $students; + $mappingtable->showsublogcolumn = $showsublogcolumn; + $mappingtables[] = $mappingtable; } - // Check if there is any task info to display. - $additionalinfo = $lasttasktext || $hastaskinprogress; - - // Check if the user has the capability to see the submission log column. - $showsublogcolumn = has_capability('local/sitsgradepush:showsubmissionlogcolumn', \context_course::instance($courseid)); + // Handle invalid students. + if (!empty($assessmentdata['invalidstudents']->students)) { + $assessmentdata['invalidstudents']->tabletitle = $assessmentdata['invalidstudents']->formattedname; + $assessmentdata['invalidstudents']->showsublogcolumn = $showsublogcolumn; + } // Render the table. - return $this->output->render_from_template('local_sitsgradepush/assessmentgrades', [ - 'mappingid' => $mappingid, - 'tabletitle' => $mapping->formattedname, - 'students' => $students, - 'lasttask' => $lasttasktext, - 'additionalinfo' => $additionalinfo, - 'showsublogcolumn' => $showsublogcolumn, + return $this->output->render_from_template('local_sitsgradepush/marks_transfer_history_page', [ + 'module-delivery-tables' => $mappingtables, + 'transfer-all-button-label' => get_string('label:pushgrade', 'local_sitsgradepush'), + 'latest-transferred-text' => $this->get_latest_tranferred_text($assessmentdata['mappings']), + 'invalid-students' => !empty($assessmentdata['invalidstudents']->students) ? $assessmentdata['invalidstudents'] : null, + 'async' => get_config('local_sitsgradepush', 'async'), ]); } @@ -149,7 +151,7 @@ public function render_dashboard(array $moduledeliveries, int $courseid) : strin // Set default value for the select module delivery dropdown list. $options[] = (object) ['value' => 'none', 'name' => 'NONE']; - $moduledeliverytables = ''; + $moduledeliverytables = []; // Prepare the content for each module delivery table. foreach ($moduledeliveries as $moduledelivery) { // Set options for the select module delivery dropdown list. @@ -172,7 +174,7 @@ public function render_dashboard(array $moduledeliveries, int $courseid) : strin // Add the select source url. $selectsourceurl = new \moodle_url( '/local/sitsgradepush/select_source.php', - ['courseid' => $courseid, 'mabid' => $componentgrade->id] + ['courseid' => $courseid, 'mabid' => $componentgrade->id, 'source' => manager::SOURCE_EXISTING_ACTIVITY] ); $componentgrade->selectsourceurl = $selectsourceurl->out(false); @@ -185,14 +187,13 @@ public function render_dashboard(array $moduledeliveries, int $courseid) : strin // Get the assessment mapping status. if ($coursemodule = get_coursemodule_from_id('', $componentgrade->coursemoduleid)) { - $students = $this->manager->get_students_in_assessment_mapping($componentgrade->assessmentmappingid); - $numberofstudents = 0; - if (!empty($students)) { - $numberofstudents = count($students); - } + $assessmentdata = $this->manager->get_assessment_data( + $componentgrade->coursemoduleid, + $componentgrade->assessmentmappingid + ); $assessmentmapping = new \stdClass(); - $assessmentmapping->numberofstudents = $numberofstudents; + $assessmentmapping->markstotransfer = $assessmentdata->markscount ?? 0; $assessmentmapping->id = $componentgrade->assessmentmappingid; $assessmentmapping->type = get_module_types_names()[$coursemodule->modname]; $assessmentmapping->name = $coursemodule->name; @@ -203,68 +204,34 @@ public function render_dashboard(array $moduledeliveries, int $courseid) : strin $assessmentmapping->url = $coursemoduleurl->out(false); $transferhistoryurl = new \moodle_url('/local/sitsgradepush/index.php', ['id' => $coursemodule->id]); $assessmentmapping->transferhistoryurl = $transferhistoryurl->out(false); - $assessmentmapping->status = - $this->get_assessment_mapping_status_icon($componentgrade->assessmentmappingid); - $assessmentmapping->statusicon = $assessmentmapping->status->statusicon; $componentgrade->assessmentmapping = $assessmentmapping; - $componentgrade->disablechangesourcebutton = - $this->disable_change_source_button($componentgrade->assessmentmappingid) ? ' disabled' : ''; } else { throw new \moodle_exception('error:invalidcoursemoduleid', 'local_sitsgradepush'); } } } - // Render the module delivery table. - $moduledeliverytables .= $this->output->render_from_template( - 'local_sitsgradepush/module_delivery_table', - [ - 'tableid' => $tableid, - 'moduledelivery' => $tableid, - 'academicyear' => $moduledelivery->academicyear, - 'level' => $moduledelivery->level, - 'graduatetype' => $moduledelivery->graduatetype, - 'mapcode' => $moduledelivery->mapcode, - 'componentgrades' => $componentgrades, - ]); - } + $moduledeliverytable = new \stdClass(); + $moduledeliverytable->tableid = $tableid; + $moduledeliverytable->moduledelivery = $tableid; + $moduledeliverytable->academicyear = $moduledelivery->academicyear; + $moduledeliverytable->level = $moduledelivery->level; + $moduledeliverytable->graduatetype = $moduledelivery->graduatetype; + $moduledeliverytable->mapcode = $moduledelivery->mapcode; + $moduledeliverytable->componentgrades = $componentgrades; - // Render the module delivery selector. - $moduledeliveryselector = $this->output->render_from_template( - 'local_sitsgradepush/selectelement', - [ - 'selectid' => 'module-delivery-selector', - 'label' => get_string('label:jumpto', 'local_sitsgradepush'), - 'options' => $options, - ] - ); + $moduledeliverytables[] = $moduledeliverytable; + } - // Render the push all button. - $pushallbutton = $this->output->render_from_template( - 'local_sitsgradepush/button', + return $this->output->render_from_template( + 'local_sitsgradepush/dashboard', [ - 'id' => 'push-all-button', - 'name' => get_string('label:pushall', 'local_sitsgradepush'), - 'disabled' => '', - 'class' => 'sitgradepush-btn-center', + 'module-delivery-tables' => $moduledeliverytables, + 'jump-to-options' => $options, + 'jump-to-label' => get_string('label:jumpto', 'local_sitsgradepush'), + 'transfer-all-button-label' => get_string('label:pushall', 'local_sitsgradepush'), ] ); - - // Render the back to top button. - $backtotopbutton = $this->output->render_from_template('local_sitsgradepush/back_to_top_button', []); - - // Return the combined HTML. - return $moduledeliveryselector . $moduledeliverytables . $pushallbutton . $backtotopbutton; - } - - /** - * Render the select source page. - * - * @return string Rendered HTML - * @throws \moodle_exception - */ - public function render_select_source_page() { - return $this->output->render_from_template('local_sitsgradepush/select_source_page', []); } /** @@ -275,7 +242,7 @@ public function render_select_source_page() { * @throws \dml_exception * @throws \moodle_exception */ - public function render_existing_activity_page(array $param) { + public function render_existing_activity_page(array $param): bool|string { // Make sure we have the required parameters. if (empty($param['courseid']) || empty($param['mabid'])) { throw new \moodle_exception('error:missingparams', 'local_sitsgradepush'); @@ -351,57 +318,29 @@ private function get_label_html(int $errortype = null) : string { } /** - * Get the last push task time. - * - * @param int $assessmentmappingid Assessment mapping ID - * @return string|null Last push task time - * @throws \coding_exception - * @throws \dml_exception - */ - private function get_last_push_task_time(int $assessmentmappingid) { - // Add last task details to the mapping if any. - $time = null; - if ($lasttask = taskmanager::get_last_finished_push_task($assessmentmappingid)) { - $time = get_string( - 'label:lastpushtext', - 'local_sitsgradepush', [ - 'statustext' => $lasttask->statustext, - 'date' => date('d/m/Y', $lasttask->timeupdated), - 'time' => date('g:i:s a', $lasttask->timeupdated), ]); - } - - return $time; - } - - /** - * Get the assessment mapping status icon. + * Get the latest transferred text for the transfer history page. * - * @param int $assessmentmappingid Assessment mapping ID - * @return \stdClass Assessment mapping status icon + * @param array $mappings + * @return \lang_string|string * @throws \coding_exception * @throws \dml_exception */ - public function get_assessment_mapping_status_icon(int $assessmentmappingid) { - $status = manager::get_manager()->has_grades_pushed($assessmentmappingid) ? - self::PUSH_STATUS_ICON_HAS_PUSH_RECORDS : self::PUSH_STATUS_ICON_NO_PUSH_RECORDS; - - $result = new \stdClass(); - $result->status = $status; - switch ($status) { - case self::PUSH_STATUS_ICON_HAS_PUSH_RECORDS: - $result->statusicon = ''; - $result->statustext = get_string('pushrecordsexist', 'local_sitsgradepush'); - break; - default: - $result->statusicon = ''; - $result->statustext = get_string('pushrecordsnotexist', 'local_sitsgradepush'); - break; + public function get_latest_tranferred_text(array $mappings) { + $lasttasktext = ''; + $lasttasktime = 0; + foreach ($mappings as $mapping) { + $lasttask = taskmanager::get_last_finished_push_task($mapping->id); + if ($lasttask && $lasttask->timeupdated && $lasttask->timeupdated > $lasttasktime) { + $lasttasktime = $lasttask->timeupdated; + $lasttasktext = get_string( + 'label:lastpushtext', + 'local_sitsgradepush', [ + 'date' => date('d/m/Y', $lasttasktime), + 'time' => date('g:i:s a', $lasttasktime), ]); + } } - return $result; + return $lasttasktext; } /** diff --git a/classes/taskmanager.php b/classes/taskmanager.php index 397c8b7..7ebba57 100644 --- a/classes/taskmanager.php +++ b/classes/taskmanager.php @@ -178,23 +178,18 @@ public static function get_last_finished_push_task(int $assessmentmappingid): mi * Get the last push task time. * * @param int $assessmentmappingid Assessment mapping ID - * @return string|null Last push task time + * @return int|null Last push task time * @throws \coding_exception * @throws \dml_exception */ - public static function get_last_push_task_time(int $assessmentmappingid) { - // Add last task details to the mapping if any. - $time = null; + public static function get_last_push_task_time(int $assessmentmappingid): ?int { if ($lasttask = self::get_last_finished_push_task($assessmentmappingid)) { - $time = get_string( - 'label:lastpushtext', - 'local_sitsgradepush', [ - 'statustext' => $lasttask->statustext, - 'date' => date('d/m/Y', $lasttask->timeupdated), - 'time' => date('g:i:s a', $lasttask->timeupdated), ]); + if (!empty($lasttask->timeupdated)) { + return $lasttask->timeupdated; + } } - return $time; + return null; } /** diff --git a/dashboard.php b/dashboard.php index c93ab21..c366cbc 100644 --- a/dashboard.php +++ b/dashboard.php @@ -83,6 +83,8 @@ // Render the dashboard. if (!empty($result)) { echo '
'; + echo '

' . get_string('dashboard:header', 'local_sitsgradepush') . '

+

' . get_string('dashboard:header:desc', 'local_sitsgradepush') . '

'; echo $renderer->render_dashboard($result, $courseid); echo '
'; } else { @@ -95,8 +97,6 @@ 'init', [ $courseid, - get_config('local_sitsgradepush', 'sync_threshold'), - get_config('local_sitsgradepush', 'async'), $CFG->version, ] ); diff --git a/db/services.php b/db/services.php index 7717d83..945fc81 100644 --- a/db/services.php +++ b/db/services.php @@ -41,20 +41,6 @@ 'type' => 'write', 'loginrequired' => true, ], - 'local_sitsgradepush_get_transfer_students' => [ - 'classname' => 'local_sitsgradepush\external\get_transfer_students', - 'description' => 'Get transfer students for a given assessment mapping', - 'ajax' => true, - 'type' => 'read', - 'loginrequired' => true, - ], - 'local_sitsgradepush_transfer_mark_for_student' => [ - 'classname' => 'local_sitsgradepush\external\transfer_mark_for_student', - 'description' => 'Transfer mark for a given student', - 'ajax' => true, - 'type' => 'write', - 'loginrequired' => true, - ], 'local_sitsgradepush_get_assessments_update' => [ 'classname' => 'local_sitsgradepush\external\get_assessments_update', 'description' => 'Get assessment updates for a given course / course module', diff --git a/index.php b/index.php index 5d2f43a..03b5a6e 100644 --- a/index.php +++ b/index.php @@ -38,6 +38,9 @@ // Course module ID. $coursemoduleid = required_param('id', PARAM_INT); +// Initiate grade push and show push result. +$pushgrade = optional_param('pushgrade', 0, PARAM_INT); + // Get manager and check course module exists. if (!$coursemodule = get_coursemodule_from_id(null, $coursemoduleid)) { throw new moodle_exception('course module not found.', 'local_sitsgradepush'); @@ -78,26 +81,38 @@ // Get page content. $content = $manager->get_assessment_data($coursemoduleid); -$mappingids = []; +// Check if asynchronous grade push is enabled. +$async = get_config('local_sitsgradepush', 'async'); if (!empty($content)) { - // Display grade push records for each mapping. - foreach ($content['mappings'] as $mapping) { - $mappingids[] = $mapping->id; - echo $renderer->render_assessment_push_status_table($mapping, $coursemodule->course); + // Transfer marks if it has a pushgrade is set. + if (!$async && $pushgrade == 1) { + // Loop through each mapping. + foreach ($content['mappings'] as $mapping) { + // Skip if there is no student in the mapping. + if (empty($mapping->students)) { + continue; + } + // Push grades for each student in the mapping. + foreach ($mapping->students as $student) { + $manager->push_grade_to_sits($mapping, $student->userid); + $manager->push_submission_log_to_sits($mapping, $student->userid); + } + } + + // Refresh data after completed all pushes. + $content = $manager->get_assessment_data($coursemoduleid); } - // Display invalid students. - if (!empty($content['invalidstudents']->students)) { - echo $renderer->render_assessment_push_status_table($content['invalidstudents'], $coursemodule->course); - } + // Render the page. + echo $renderer->render_marks_transfer_history_page($content, $coursemodule->course); } else { echo '

' . get_string('error:assessmentisnotmapped', 'local_sitsgradepush') . '

'; } echo ''; // Initialize javascript. -$PAGE->requires->js_call_amd('local_sitsgradepush/sitsgradepush', 'init', [$coursemodule->course, $coursemoduleid, $mappingids]); +$PAGE->requires->js_call_amd('local_sitsgradepush/sitsgradepush', 'init', [$coursemodule->course, $coursemoduleid]); // And the page footer. echo $OUTPUT->footer(); diff --git a/lang/en/local_sitsgradepush.php b/lang/en/local_sitsgradepush.php index 3c3e591..35e8105 100644 --- a/lang/en/local_sitsgradepush.php +++ b/lang/en/local_sitsgradepush.php @@ -51,12 +51,12 @@ $string['settings:sync_threshold'] = 'Sync Threshold'; $string['settings:sync_threshold:desc'] = 'The threshold to allow for running synchronous mark transfer task'; $string['label:gradepushassessmentselect'] = 'Select SITS assessment to link to'; -$string['label:jumpto'] = 'Jump to: '; +$string['label:jumpto'] = 'Jump to'; $string['label:pushall'] = 'Transfer All'; $string['label:reassessmentselect'] = 'Re-assessment'; $string['label:pushgrade'] = 'Transfer Marks'; $string['label:ok'] = 'OK'; -$string['label:lastpushtext'] = 'Last scheduled transfer task {$a->statustext} {$a->date} at {$a->time}'; +$string['label:lastpushtext'] = 'Last transferred {$a->date} at {$a->time}'; $string['option:none'] = 'NONE'; $string['gradepushassessmentselect'] = 'Select SITS assessment'; $string['gradepushassessmentselect_help'] = 'Select SITS assessment to link to this activity.'; @@ -79,19 +79,24 @@ $string['index:lastsublogtransfer'] = 'Last Submission Log Transfer'; // Marks transfer dashboard page. -$string['dashboard:header'] = 'SITS Marks Transfer Dashboard'; -$string['dashboard:moduledelivery'] = 'MODULE DELIVERY: {$a}'; +$string['dashboard:header'] = 'SITS assessment mapping and mark transfer'; +$string['dashboard:header:desc'] = 'Map SITS assessments to Moodle activites and transfer marks from Moodle to SITS.'; +$string['dashboard:moduledelivery'] = 'MODULE DELIVERY'; $string['dashboard:academicyear'] = 'ACADEMIC YEAR: {$a}'; $string['dashboard:level'] = 'LEVEL: {$a}'; $string['dashboard:mapcode'] = 'MAP CODE: {$a}'; $string['dashboard:seq'] = 'SEQ'; -$string['dashboard:title'] = 'TITLE'; -$string['dashboard:weight'] = 'WEIGHT'; +$string['dashboard:sits_assessment'] = 'SITS assessment'; +$string['dashboard:weight'] = 'Weight'; $string['dashboard:asttype'] = 'AST TYPE'; -$string['dashboard:source'] = 'SOURCE'; +$string['dashboard:source'] = 'Source'; +$string['dashboard:moodle_activity'] = 'Moodle activity'; +$string['dashboard:marks_to_transfer'] = 'Marks to transfer'; +$string['dashboard:view_marks_to_transfer'] = 'View marks to transfer'; $string['dashboard:actions'] = 'ACTIONS'; -$string['dashboard:transfermark'] = 'Transfer Mark'; +$string['dashboard:transfermarks'] = 'Transfer marks'; $string['dashboard:changesource'] = 'Change Source'; +$string['dashboard:marks_transfer_in_progress'] = 'Marks transfer in progress'; // Select source page. $string['selectsource:header'] = 'Select Source'; @@ -181,3 +186,10 @@ $string['email:content:success'] = 'Marks Transfer Task from {$a->activityname} to {$a->mab} has been {$a->status}.

You can check the marks transfer history for this task here: {$a->activityname} - {$a->mab}'; $string['email:content:fail'] = 'Marks Transfer Task from {$a->activityname} to {$a->mab} has been {$a->status}.

Please try again later.'; $string['email:unknown'] = 'unknown'; + +// Confirmation Modal strings. +$string['confirmmodal:header'] = 'Confirm mark transfer?'; +$string['confirmmodal:body:partone'] = 'Caution: after a student mark has been transferred to SITS, it cannot be recalled or overwritten. Should you need to make a change to a transferred mark, you will need to do this in the usual way in Portico.'; +$string['confirmmodal:body:parttwo'] = 'Please be patient waiting for the marks transfer job to complete as a large set of marks can take some time. You can check the marks transfer history table for progress but will also receive an email notification once the marks transfer job has been completed.'; +$string['confirmmodal:confirm'] = 'Confirm'; +$string['confirmmodal:cancel'] = 'Cancel'; diff --git a/styles.css b/styles.css index d0e208c..7bd3623 100644 --- a/styles.css +++ b/styles.css @@ -1,125 +1,7 @@ -.sitgradepush-btn-center { - text-align: center; +.sitsgradepush-history-table-container { + margin-top: 40px; } -#dashboard-progress-bar-sync { - height: 20px; -} - -.sitsgradepush-history-table .progress { - height: 15px; - width: 150px; -} - -.sitsgradepush-history-table .spinner-border { - margin-right: 10px; -} - -.sitsgradepush-dasboard .selector-container { - margin-bottom: 20px; -} - -.sitsgradepush-dasboard .module-delivery-table { - text-align: center; - margin-bottom: 50px; - background-color: #fff5ea; -} - -.sitsgradepush-dasboard .module-delivery-table .seq-col { - width: 5%; -} - -.sitsgradepush-dasboard .module-delivery-table .title-col { - width: 40%; -} - -.sitsgradepush-dasboard .module-delivery-table .weight-col { - width: 5%; -} - -.sitsgradepush-dasboard .module-delivery-table .mark-col { - width: 35%; -} - -.sitsgradepush-dasboard .module-delivery-table .action-col { - width: 15%; -} - -/* Add CSS styles for the "Back to Top" button */ -.sitsgradepush-dasboard .back-to-top { - display: none; - position: fixed; - bottom: 20px; /* Adjust the distance from the bottom as needed */ - right: 20px; /* Adjust the distance from the right as needed */ - z-index: 999; - padding: 10px 20px; - background-color: #007bff; /* Button background color */ - color: #fff; /* Button text color */ - border: none; - border-radius: 4px; - cursor: pointer; - font-size: 16px; -} - -.sitsgradepush-dasboard .back-to-top:hover { - background-color: #0056b3; /* Hover background color */ -} - -.sitsgradepush-dasboard .progress { - margin: 10px 5px 0px 5px; -} - -/* Styles for select source page */ -/* Styles scoped to cards within a specific .sitsgradepush-select-source container */ -.sitsgradepush-select-source .card-custom { - cursor: pointer; - display: flex; - justify-content: center; - align-items: center; - margin-bottom: 30px; - height: 200px; - width: 100%; - transition: transform 0.3s, box-shadow 0.3s; - border-radius: 15px; - box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); - color: #333; -} - -/* Card background colors */ -.sitsgradepush-select-source .card-1 { - background-color: #e0bbe4; -} -.sitsgradepush-select-source .card-2 { - background-color: #ffdfd3; -} -.sitsgradepush-select-source .card-3 { - background-color: #bee7e9; -} -.sitsgradepush-select-source .card-4 { - background-color: #ffebbb; -} - -/* Hover effect for cards */ -.sitsgradepush-select-source .card-custom:hover { - transform: scale(1.05); - box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2); -} - -/* Centering the card container on the page */ -.sitsgradepush-select-source .centered-container { - min-height: 100vh; - display: flex; - flex-direction: column; - justify-content: flex-start; - margin-top: 100px; - align-items: center; -} - -/* Max width for the container */ -.sitsgradepush-select-source .container { - max-width: 90%; -} - -.sitsgradepush-select-source .hidden { - display: none; +.sitsgradepush-dasboard .module-delivery-table .marks-col { + width: 200px; } diff --git a/templates/confirmation_modal.mustache b/templates/confirmation_modal.mustache new file mode 100644 index 0000000..948d4f7 --- /dev/null +++ b/templates/confirmation_modal.mustache @@ -0,0 +1,54 @@ +{{! + This file is part the Local SITS Grade Push plugin for Moodle + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template local_sitsgradepush/confirmation_modal + + Template for displaying a confirmation modal when transfer button is clicked. + + Classes required for JS: + * none + + Data attributes required for JS: + * data-assessmentmappingid - String, assessment mapping id. + * data-async - Integer, async value. + + Context variables required for this template: + * none + + Example context (json): + {} +}} + + diff --git a/templates/dashboard.mustache b/templates/dashboard.mustache new file mode 100644 index 0000000..cb84192 --- /dev/null +++ b/templates/dashboard.mustache @@ -0,0 +1,218 @@ +{{! + This file is part the Local SITS Grade Push plugin for Moodle + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template local_sitsgradepush/dashboard + + Template for displaying the marks transfer dashboard page. + + Classes required for JS: + * marks-col-field + * marks-container + * js-btn-transfer-marks + * task-status-container + * hidden + * progress + + Data attributes required for JS: + * data-assessmentmappingid - String, assessment mapping id. + * data-markscount - Integer, number of marks to transfer. + * data-task-running - String, indicate if there is a task running for this mapping. + + Context variables required for this template: + * jump-to-label - String, jump to label. + * jump-to-options - Array, array of jump to options. + * name - String, module delivery name. + * module-delivery-tables - Array, array of module delivery tables. + * tableid - String, table id. + * modcode - String, module code. + * academicyear - String, academic year. + * level - String, level. + * graduatetype - String, graduate type. + * mapcode - String, map code. + * componentgrades - Array, array of component grades. + * mabseq - String, mab sequence. + * mabname - String, mab name. + * mabperc - String, mab percentage. + * astcode - String, ast code. + * assessmentmapping - Array, array of assessment mapping. + * id - String, assessment mapping id. + * type - String, assessment mapping type. + * url - String, assessment mapping url. + * statusicon - String, assessment mapping status icon. + * selectsourceurl - String, select source url. + * transferhistoryurl - String, transfer history url. + * markstotransfer - String, marks to transfer. + * transfer-all-button-label - String, button label. + + Example context (json): + { + "jump-to-label": "Jump to", + "jump-to-options": + [ + { + "name": "PHAY0063-2022-T1-A7P-001 Coursework 4000 word written case studies (50%)" + } + ], + "module-delivery-tables": + [ + { + "tableid": "PHAY0063-A7P-T1-2022", + "moduledelivery": "PHAY0063-A7P-T1-2022", + "academicyear": "2022", + "componentgrades": + { + "21": + { + "mabseq": "001", + "astcode": "CN01", + "mabperc": "50", + "mabname": "Coursework 4000 word written case studies (50%)", + "assessmentmapping": + { + "id": "39", + "type": "Assignment", + "name": "Test assignment 2", + "url": "http://test.m4.local:4001/mod/assign/view.php?id=960", + "markstotransfer": "1", + "statusicon": "", + "selectsourceurl": "http://test.m4.local:4001/local/sitsgradepush/select_source.php?courseid=2&mabid=22", + "transferhistoryurl": "http://test.m4.local:4001/local/sitsgradepush/index.php?id=22" + } + } + }, + "mapcode": "PHAY0063A7PE", + "level": "7", + "graduatetype": "POSTGRADUATE" + } + ], + "transfer-all-button-label": "Transfer All" + } +}} + +{{#module-delivery-tables}} +
+
+
+
+ {{#str}} dashboard:moduledelivery, local_sitsgradepush {{/str}} +

{{{ moduledelivery }}}

+ {{#str}} dashboard:level, local_sitsgradepush, {{level}} {{/str}} {{graduatetype}} +
+
+ Map code
+
{{{ mapcode }}}
+
+
+
+ + + + + + + + + + + {{#componentgrades}} + + + + + + + {{/componentgrades}} + {{^componentgrades}} + + + + {{/componentgrades}} + +
{{#str}} dashboard:sits_assessment, local_sitsgradepush {{/str}}{{#str}} dashboard:weight, local_sitsgradepush {{/str}}{{#str}} dashboard:moodle_activity, local_sitsgradepush {{/str}}{{#str}} dashboard:marks_to_transfer, local_sitsgradepush {{/str}}
+ {{mabseq}} + {{mabname}} + {{mabperc}} + {{#assessmentmapping}} +

+ {{type}} + {{name}} +

+ {{/assessmentmapping}} + + {{#assessmentmapping}}Edit {{/assessmentmapping}} + {{^assessmentmapping}}Add {{/assessmentmapping}} + activity + +
+ {{#assessmentmapping}} +
+
+

+ {{markstotransfer}} +

+ {{#markstotransfer}} + + {{/markstotransfer}} +
+ +
+ {{/assessmentmapping}} +
{{#str}} error:no_mab_found, local_sitsgradepush {{/str}}
+{{/module-delivery-tables}} + +
+ {{> local_sitsgradepush/transfer_all_button }} +
+ +{{! Modal }} +{{> local_sitsgradepush/confirmation_modal }} + +{{#js}} + require(['jquery'], function($) { + $('.js-btn-transfer-marks').on('click', function() { + let mappingid = $(this).closest('.marks-col-field').attr('data-assessmentmappingid'); + $('#js-transfer-modal-button').attr('data-assessmentmappingid', mappingid); + }); + + $('.js-btn-transfer-all-marks').on('click', function() { + let mappingid = $(this).attr('data-assessmentmappingid'); + $('#js-transfer-modal-button').attr('data-assessmentmappingid', mappingid); + }); + }); +{{/js}} diff --git a/templates/marks_transfer_history_page.mustache b/templates/marks_transfer_history_page.mustache new file mode 100644 index 0000000..2d296a6 --- /dev/null +++ b/templates/marks_transfer_history_page.mustache @@ -0,0 +1,121 @@ +{{! + This file is part the Local SITS Grade Push plugin for Moodle + Moodle is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Moodle is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Moodle. If not, see . +}} +{{! + @template local_sitsgradepush/marks_transfer_history_page + + Template for displaying the grade push history for an assessment. + + Classes required for JS: + * none + + Data attributes required for JS: + * none + + Context variables required for this template: + * transfer-all-button-label - String, button label. + * latest-transferred-text - String, latest transferred datetime. + * module-delivery-tables - Array of module delivery tables. + * tabletitle - String, table title. + * students - Array of students. + * firstname - String, student first name. + * lastname - String, student last name. + * idnumber - String, student id number. + * marks - String, student marks. + * handindatetime - String, student hand in datetime. + * lastgradepushresultlabel - String, student last grade push result label. + * lastgradepushtime - String, student last grade push time. + * lastsublogpushresultlabel - String, student last submission log push result label. + * lastsublogpushtime - String, student last submission log push time. + * invalid-students - Array of invalid students. + * tabletitle - String, table title. + * students - Array of students. + * firstname - String, student first name. + * lastname - String, student last name. + * idnumber - String, student id number. + * marks - String, student marks. + * handindatetime - String, student hand in datetime. + * lastgradepushresultlabel - String, student last grade push result label. + * lastgradepushtime - String, student last grade push time. + * lastsublogpushresultlabel - String, student last submission log push result label. + * lastsublogpushtime - String, student last submission log push time. + + Example context (json): + { + "transfer-all-button-label":"Transfer Marks", + "latest-transferred-text":"Last transferred 29/01/2024 at 11:00:49 pm", + "module-delivery-tables":[{ + "tabletitle":"PHAY0063-2022-T1-A7P-001 Coursework 4000 word written case studies (50%)", + "mappingid":"21", + "students":[{ + "firstname":"Test", + "lastname":"User", + "idnumber":"1234567", + "marks":"0", + "handindatetime":"2019-01-01 00:00:00", + "lastgradepushresultlabel":"Success ", + "lastgradepushtime":"2019-01-01 00:00:00", + "lastsublogpushresultlabel":"Success ", + "lastsublogpushtime":"2019-01-01 00:00:00" + }] + }], + "invalid-students":[{ + "tabletitle":"Students not valid for the mapped assessment components", + "students":[{ + "firstname":"Invalid", + "lastname":"Student", + "idnumber":"7654321", + "marks":"-", + "handindatetime":"-", + "lastgradepushresultlabel":"", + "lastgradepushtime":"-", + "lastsublogpushresultlabel":"", + "lastsublogpushtime":"-" + }] + }] + } +}} + + +
+ {{> local_sitsgradepush/transfer_all_button }} +
+ + +

{{latest-transferred-text}}

+ + +{{#module-delivery-tables}} + {{> local_sitsgradepush/marks_transfer_history_table }} + {{^students}} +

{{#str}} error:nostudentfoundformapping, local_sitsgradepush {{/str}}

+ {{/students}} +{{/module-delivery-tables}} + + +{{#invalid-students}} + {{> local_sitsgradepush/marks_transfer_history_table }} +{{/invalid-students}} + + +{{> local_sitsgradepush/confirmation_modal }} + +{{#js}} + require(['jquery'], function($) { + $('.js-btn-transfer-all-marks').on('click', function() { + $('#js-transfer-modal-button').attr('data-async', $(this).attr('data-async')); + }); + }); +{{/js}} \ No newline at end of file diff --git a/templates/assessmentgrades.mustache b/templates/marks_transfer_history_table.mustache similarity index 65% rename from templates/assessmentgrades.mustache rename to templates/marks_transfer_history_table.mustache index e2a294a..dfa283d 100644 --- a/templates/assessmentgrades.mustache +++ b/templates/marks_transfer_history_table.mustache @@ -14,25 +14,30 @@ along with Moodle. If not, see . }} {{! - @template local_sitsgradepush/assessmentgrades + @template local_sitsgradepush/marks_transfer_history_table - Template for displaying the grade push history for an assessment. + Template for displaying mapping table on the marks transfer history page. Classes required for JS: - * none + * progress-container - The container for the progress bar. Data attributes required for JS: - * none + * data-assessmentmappingid - String, assessment mapping id. + * data-markscount - Integer, marks count. Context variables required for this template: * tabletitle - String, The assessment component's name - * additionalinfo - Boolean, whether to display additional information + * mappingid - String, assessment mapping id. + * markscount - Integer, marks count. + * showsublogcolumn - Boolean, whether to show the submission log column. * students - Array, array of students Example context (json): { "tabletitle":"PHAY0063-2022-T1-A7P-001 Coursework 4000 word written case studies (50%)", - "additionalinfo":false, + "mappingid":"21", + "markscount":"1", + "showsublogcolumn":true, "students":[{ "firstname":"Test", "lastname":"User", @@ -46,21 +51,19 @@ }] } }} - - +
+
{{tabletitle}}
+ +
{{tabletitle}}
- {{#additionalinfo}} - - - - {{/additionalinfo}} @@ -87,6 +90,4 @@ {{/students}}
-
-
{{lasttask}}
-
-
-
-
-
Student Portico number
-{{^students}} -

{{#str}} error:nostudentfoundformapping, local_sitsgradepush {{/str}}

-{{/students}} \ No newline at end of file + \ No newline at end of file diff --git a/templates/module_delivery_table.mustache b/templates/module_delivery_table.mustache deleted file mode 100644 index 93fa1ea..0000000 --- a/templates/module_delivery_table.mustache +++ /dev/null @@ -1,169 +0,0 @@ -{{! - This file is part the Local SITS Grade Push plugin for Moodle - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template local_sitsgradepush/module_delivery_table - - Template for displaying a module delivery information. - - Classes required for JS: - * none - - Data attributes required for JS: - * data-assessmentmappingid - String, assessment mapping id. - - Context variables required for this template: - * tableid - String, table id. - * modcode - String, module code. - * academicyear - String, academic year. - * level - String, level. - * graduatetype - String, graduate type. - * mapcode - String, map code. - * componentgrades - Array, array of component grades. - * mabseq - String, mab sequence. - * mabname - String, mab name. - * mabperc - String, mab percentage. - * astcode - String, ast code. - * assessmentmapping - Array, array of assessment mapping. - * id - String, assessment mapping id. - * type - String, assessment mapping type. - * url - String, assessment mapping url. - * statusicon - String, assessment mapping status icon. - * selectsourceurl - String, select source url. - * numberofstudents - String, number of students. - * disablechangesourcebutton - String, value to insert into the button, for 'disabled' or not. - - Example context (json): - { - "tableid": "PHAY0063-A7P-T1-2022", - "moduledelivery": "PHAY0063-A7P-T1-2022", - "academicyear": "2022", - "componentgrades": - { - "21": - { - "mabseq": "001", - "astcode": "CN01", - "mabperc": "50", - "mabname": "Coursework 4000 word written case studies (50%)", - "assessmentmapping": - { - "id": "39", - "type": "Assignment", - "name": "Test assignment 2", - "url": "http://test.m4.local:4001/mod/assign/view.php?id=960", - "statusicon": "", - "selectsourceurl": "http://test.m4.local:4001/local/sitsgradepush/select_source.php?courseid=2&mabid=22", - "transferhistoryurl": "http://test.m4.local:4001/local/sitsgradepush/index.php?id=22", - "numberofstudents": "10" - }, - "disablechangesourcebutton": " disabled" - } - }, - "mapcode": "PHAY0063A7PE", - "level": "7", - "graduatetype": "POSTGRADUATE" - } -}} - - - - - - - - - - - - - - - - - - {{#componentgrades}} - - - - - - - - {{/componentgrades}} - {{^componentgrades}} - - - - {{/componentgrades}} - -
-
-
{{#str}} dashboard:moduledelivery, local_sitsgradepush, {{moduledelivery}} {{/str}}
-
{{#str}} dashboard:academicyear, local_sitsgradepush, {{academicyear}} {{/str}}
-
-
-
-
{{#str}} dashboard:mapcode, local_sitsgradepush, {{mapcode}} {{/str}}
-
{{#str}} dashboard:level, local_sitsgradepush, {{level}} {{/str}} {{graduatetype}}
-
-
{{#str}} dashboard:seq, local_sitsgradepush {{/str}}{{#str}} dashboard:title, local_sitsgradepush {{/str}}{{#str}} dashboard:weight, local_sitsgradepush {{/str}}{{#str}} dashboard:source, local_sitsgradepush {{/str}}{{#str}} dashboard:actions, local_sitsgradepush {{/str}}
{{mabseq}}{{mabname}}{{mabperc}} - {{#assessmentmapping}} - {{name}} - {{/assessmentmapping}} - {{^assessmentmapping}} - - {{/assessmentmapping}} - -
- - - - - - - - {{#assessmentmapping}} - {{{statusicon}}} - {{/assessmentmapping}} - {{^assessmentmapping}} - - {{/assessmentmapping}} - -
-
{{#str}} error:no_mab_found, local_sitsgradepush {{/str}}
diff --git a/templates/select_source_page.mustache b/templates/select_source_page.mustache deleted file mode 100644 index 500f61c..0000000 --- a/templates/select_source_page.mustache +++ /dev/null @@ -1,68 +0,0 @@ -{{! - This file is part the Local SITS Grade Push plugin for Moodle - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template local_sitsgradepush/select_source_page - - Template for displaying source options. - - Classes required for JS: - * none - - Data attributes required for JS: - * none - - Context variables required for this template: - * none - - Example context (json): {} -}} -

Select Source

-
-
-
-
-
-
-
- {{#str}} selectsource:existing, local_sitsgradepush {{/str}} -
-
-
-
- -
-
- -
-
- -
-
-
-
-
diff --git a/templates/selectelement.mustache b/templates/transfer_all_button.mustache similarity index 54% rename from templates/selectelement.mustache rename to templates/transfer_all_button.mustache index 3cc4e3b..5bda1bc 100644 --- a/templates/selectelement.mustache +++ b/templates/transfer_all_button.mustache @@ -14,42 +14,24 @@ along with Moodle. If not, see . }} {{! - @template local_sitsgradepush/selectelement + @template local_sitsgradepush/transfer_all_button - Template for displaying a select element. + Template for displaying the transfer all button. Classes required for JS: - * none + * js-btn-transfer-all-marks Data attributes required for JS: - * none + * data-assessmentmappingid - String, assessment mapping id. + * data-async - Integer, async value. Context variables required for this template: - * none + * transfer-all-button-label - String, button label. Example context (json): - { - "label": "test", - "selectid": "test0", - "options": [ - { - "value": "test0", - "name": "test0", - "selected": "selected" - }, - { - "value": "test1", - "name": "test1", - "selected": "" - } - ] - } + {"transfer-all-button-label":"Transfer Marks"} }} -
- - -
+ +