// ==UserScript== // @name Punch In ou Punch Out // @namespace adds last punch // @version 1.4 // @description adds last punch // @author @aakalish // @downloadURL https://axzile.corp.amazon.com/-/carthamus/download_script/function-rollup-clocked-in-aakalish.user.js // @updateURL https://axzile.corp.amazon.com/-/carthamus/download_script/function-rollup-clocked-in-aakalish.user.js // @match https://fclm-portal.amazon.com/reports/functionRollup?* // @match https://fclm-portal.amazon.com/reports/timeOnTask?* // @match https://fclm-portal.amazon.com/reports/ppaTimeOnTask?* // @grant GM_xmlhttpRequest // @grant GM_addStyle // @require https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js // ==/UserScript== // v1.0 initial // v1.1 fixed bug // accidentally had a 0 instead of a 1 // v1.2 fixed bug // fixed punch api url based on edge cases for timeontask reports // v1.3 fixed bug // spelled things wrong (function() { 'use strict'; var clocks = { punch: {} ,add: function(p) { if( !this.punch.hasOwnProperty(p[0]) ) { this.punch[p[0]] = { time: new Date(p[5]) , type: p[4] } } else { let d = new Date(p[5]); if ( d > this.punch[p[0]].time ) { this.punch[p[0]].time = d; this.punch[p[0]].type = p[4]; }; }; } , getLastPunch: function(id) { return (this.punch.hasOwnProperty(id) ? this.punch[id] : {type: '-', time: '-'}); } }; var stTimer; var building = document.URL.split('warehouseId=')[1].split('&')[0]; var functionRollup = { show : function() { var tbls = document.getElementsByClassName('sortable result-table align-left').length > 0 ? document.getElementsByClassName('sortable result-table align-left') : document.getElementsByClassName('sortable result-table'); var is = document.URL.split('imeOnTask').length > 1 ? { id: 0, y: 2 } : { id: 1, y: 4 }; for ( var i = 0; i < tbls.length; i++ ) { if (tbls[i].querySelectorAll('.headerCompletecl').length == 0) { let thead = tbls[i].querySelector('thead'); let hd = thead.children[0].children[0].cloneNode(true); hd.innerText = 'Punched'; hd.classList.add('headerCompletecl'); thead.children[0].insertBefore(hd,thead.children[0].children[is.y]); $(hd).click(function() { let down = true; if(this.classList.contains('headerSortDown')) { down = false; } this.className = this.className.replace('headerSortUp','').replace('headerSortDown',''); this.className += down? ' headerSortDown' : ' headerSortUp'; let col = 0; let column = 0; let ch = this.parentElement.children let cur = this; while (col < ch.length) { if(cur.previousSibling.hasOwnProperty('column')) { column = cur.previousSibling.column + col; col = ch.length } else { cur = cur.previousSibling }; col++; }; let table = $(this).closest('table').find('tbody')[0]; let rows = $(this).closest('table').find('tbody').find('tr[class*=empl]'); rows = rows.length == 0 ? $(this).closest('table').find('tbody').find('tr.tot-row') : rows; rows.sort(function(a,b) { if( a.children[column].innerText > b.children[column].innerText ) { return (down? -1 : 1) * 1; } else { return (down? -1 : 1) * -1; }}) rows.each(function() { table.appendChild(this) }); }); }; let rows = document.URL.split('imeOnTask').length > 1 ? tbls[i].querySelectorAll('tr.tot-row') : tbls[i].querySelectorAll('tr.empl-all'); let loops = document.URL.split('imeOnTask').length == 1? rows.length - 1 : rows.length; for ( var j = 0; j < loops; j++ ) { let td; if( !document.getElementById('cl' + i + rows[j].children[is.id].innerText) ) { td = rows[j].children[0].cloneNode(true); } else { td = document.getElementById('cl' + i + rows[j].children[is.id].innerText); }; let punch = clocks.getLastPunch(rows[j].children[is.id].innerText) td.innerText = punch.type; td.title = punch.time.toLocaleString(); td.id = 'cl' + i + rows[j].children[is.id].innerText; rows[j].insertBefore(td,rows[j].children[is.y]); }; if ( document.URL.split('imeOnTask').length == 1 ) { if (!rows[rows.length-1].children[0].classList.contains('clock')) { rows[rows.length-1].children[0].colSpan = rows[rows.length-1].children[0].colSpan + 1; rows[rows.length-1].children[0].classList.add('clock'); }; } else { if (!tbls[i].querySelector('tr.total').children[0].classList.contains('clock')) { let row = tbls[i].querySelector('tr.total'); row.children[0].colSpan = row.children[0].colSpan + 1; row.children[0].classList.add('clock'); }; }; }; } , getPunch : function () { let startDate = document.URL.split('&startDateIntraday'); if( startDate.length > 1 ) { startDate = document.URL.split('&startDateIntraday=')[1].split('&')[0]; let endDate = (document.URL.split('&endDateIntraday=').length > 0) ? document.URL.split('&endDateIntraday=')[1].split('&')[0] : ''; let building = document.URL.split('&warehouseId=')[1].split('&')[0]; let apiUrl = 'https://fclm-portal.amazon.com/reports/employeeAttendance?reportFormat=CSV&warehouseId=' + building + '&spanType=Day&startDateDay=' + startDate; callApi(apiUrl, function(data) { functionRollup.addPunches(CSVToArray(data,',')); }); if( startDate !== endDate && endDate !== '' ) { apiUrl = 'https://fclm-portal.amazon.com/reports/employeeAttendance?reportFormat=CSV&warehouseId=' + building + '&spanType=Day&startDateDay=' + endDate; callApi(apiUrl, function(data) { functionRollup.addPunches(CSVToArray(data,',')); }); }; } else { let urlSpliter = '&processId=' + (document.URL.split('&processId=').length > 1 ? document.URL.split('&processId=')[1].split('&')[0] : ''); let apiUrl = (document.URL.split('imeOnTask').length > 1) ? '&reportFormat=CSV' + document.URL.split('imeOnTask?')[1].replace('&reportFormat=HTML','').replace(urlSpliter,'') : 'reportFormat=CSV&' + document.URL.split('functionRollup?')[1].replace('&reportFromat=HTML','').replace(urlSpliter,'').replace('reportFormat=HTML',''); apiUrl = 'https://fclm-portal.amazon.com/reports/employeeAttendance?' + apiUrl; callApi(apiUrl, function(data) { functionRollup.addPunches(CSVToArray(data,',')); }); }; } , addPunches: function(data) { for( let i = 0; i < data.length; i++ ) { clocks.add(data[i]); }; this.show(); } }; function CSVToArray( strData, strDelimiter ){ // Check to see if the delimiter is defined. If not, // then default to comma. strDelimiter = (strDelimiter || ","); // Create a regular expression to parse the CSV values. var objPattern = new RegExp( ( // Delimiters. "(\\" + strDelimiter + "|\\r?\\n|\\r|^)" + // Quoted fields. "(?:\"([^\"]*(?:\"\"[^\"]*)*)\"|" + // Standard fields. "([^\"\\" + strDelimiter + "\\r\\n]*))" ), "gi" ); // Create an array to hold our data. Give the array // a default empty first row. var arrData = [[]]; // Create an array to hold our individual pattern // matching groups. var arrMatches = null; // Keep looping over the regular expression matches // until we can no longer find a match. while (arrMatches = objPattern.exec( strData )){ // Get the delimiter that was found. var strMatchedDelimiter = arrMatches[ 1 ]; // Check to see if the given delimiter has a length // (is not the start of string) and if it matches // field delimiter. If id does not, then we know // that this delimiter is a row delimiter. if ( strMatchedDelimiter.length && strMatchedDelimiter !== strDelimiter ){ // Since we have reached a new row of data, // add an empty row to our data array. arrData.push( [] ); } var strMatchedValue; // Now that we have our delimiter out of the way, // let's check to see which kind of value we // captured (quoted or unquoted). if (arrMatches[ 2 ]){ // We found a quoted value. When we capture // this value, unescape any double quotes. strMatchedValue = arrMatches[ 2 ].replace( new RegExp( "\"\"", "g" ), "\"" ); } else { // We found a non-quoted value. strMatchedValue = arrMatches[ 3 ]; } // Now that we have our value string, let's add // it to the data array. arrData[ arrData.length - 1 ].push( strMatchedValue ); } // Return the parsed data. return( arrData ); } function callApi(url,code) { console.log(url); GM_xmlhttpRequest({ method: "GET", url: url, onload: function (response) { let data = response.responseText; if (!!code) { code(data) }; } }); }; functionRollup.getPunch(); })();