diff --git a/src/appsscript.json b/src/appsscript.json new file mode 100644 index 0000000..7b55e6a --- /dev/null +++ b/src/appsscript.json @@ -0,0 +1,21 @@ +{ + "timeZone": "Asia/Hong_Kong", + "dependencies": { + "enabledAdvancedServices": [{ + "userSymbol": "Drive", + "serviceId": "drive", + "version": "v2" + }, { + "userSymbol": "AdminDirectory", + "serviceId": "admin", + "version": "directory_v1" + }], + "libraries": [{ + "userSymbol": "TeamGroupMemberAudit", + "libraryId": "1W_ijzgRXMDNHMVEofeMZ0xoQQf7jLvJXWGjuoJls42tUem7yoe-0WAox", + "version": "4", + "developmentMode": true + }] + }, + "exceptionLogging": "STACKDRIVER" +} \ No newline at end of file diff --git a/src/timesheetAudit/.clasp.json b/src/timesheetAudit/.clasp.json new file mode 100644 index 0000000..704910b --- /dev/null +++ b/src/timesheetAudit/.clasp.json @@ -0,0 +1 @@ +{"scriptId":"1W_ijzgRXMDNHMVEofeMZ0xoQQf7jLvJXWGjuoJls42tUem7yoe-0WAox"} diff --git a/src/timesheetAudit/Reports.js b/src/timesheetAudit/Reports.js new file mode 100644 index 0000000..0cfe515 --- /dev/null +++ b/src/timesheetAudit/Reports.js @@ -0,0 +1,17 @@ +var rawInfo = onGoingCLients(); +function Report(){ + rawInfo.map(function(e) { + if (e.clientName === 'Coderbunker'){ + timeSheetReport(e); + googleGroupReport(e); + } + } + ); + // rawInfo.forEach(function(e){ + // if(e.clientName == 'Coderbunker'){ + // timeSheetReport(e); + // googleGroupReport(e); + // }; + // } + // ); +} \ No newline at end of file diff --git a/src/timesheetAudit/allUsersData.js b/src/timesheetAudit/allUsersData.js new file mode 100644 index 0000000..379ba58 --- /dev/null +++ b/src/timesheetAudit/allUsersData.js @@ -0,0 +1,53 @@ +function FreelancersInfo(freelancerInfo) { + var arrayOfMembers = JSON.parse(DriveApp.getFileById('1YgThK0YM8d_RiEc-IBPBBft0vMEeGQGg').getBlob().getDataAsString()); + var userObject = arrayOfMembers.filter( + function(currentUserObject){ + //Logger.log(currentUserObject) + if(currentUserObject['fullname'].toLowerCase().trim() === freelancerInfo.toString().toLowerCase().trim()){ + return currentUserObject; + } + /*else if (currentUserObject['altnames']!== undefined) + { + var altNamesArray = currentUserObject['altnames'].split(',').toLowerCase() + if (altNamesArray.trim().indexOf(freelancerInfo.toString().toLowerCase().trim())!== -1){ + return currentUserObject; + } + }*/ + else if(currentUserObject['email'] !== undefined && currentUserObject['email'].toString().toLowerCase().trim() === freelancerInfo.toString().toLowerCase().trim()){ + return currentUserObject; + } + /*else{ + if(currentUserObject['altemails'] !== undefined + //("'"+currentUserObject['altemails']+"'".includes(freelancerInfo.toString().toLowerCase())) + ){ + if(currentUserObject['altemails'].toString().includes(freelancerInfo.toString().toLowerCase())){ + //var altEmailsArray = currentUserObject['altemails'].toString().split(',').toLowerCase() + //if ( altEmailsArray.indexOf(freelancerInfo.toString().toLowerCase()) !== -1){ + return currentUserObject; + } + } + }*/ + } + )[0]; + return userObject; + // for (var object in arrayOfMembers){ + // var name = arrayOfMembers[object].fullname.toLowerCase(); + // // CHECK IF THERE AN ALTEMAIL OR NOT TO ASSIGN IT TO THE PERSON EMAILS + // var emails = arrayOfMembers[object].email ; + // var altemail = arrayOfMembers[object].altemails; + // if ((name.trim() || emails.trim() || altemail.trim() ) === freelancerInfo.toString().toLowerCase().trim()){ + // if (altemail !== undefined){ + // return { + // name : name, + // email: emails, + // altemails : altemail + // } + // }else{ + // return { + // name : name, + // email: emails, + // }; + // } + // } + // } +} \ No newline at end of file diff --git a/src/timesheetAudit/appsscript.json b/src/timesheetAudit/appsscript.json new file mode 100644 index 0000000..7b55e6a --- /dev/null +++ b/src/timesheetAudit/appsscript.json @@ -0,0 +1,21 @@ +{ + "timeZone": "Asia/Hong_Kong", + "dependencies": { + "enabledAdvancedServices": [{ + "userSymbol": "Drive", + "serviceId": "drive", + "version": "v2" + }, { + "userSymbol": "AdminDirectory", + "serviceId": "admin", + "version": "directory_v1" + }], + "libraries": [{ + "userSymbol": "TeamGroupMemberAudit", + "libraryId": "1W_ijzgRXMDNHMVEofeMZ0xoQQf7jLvJXWGjuoJls42tUem7yoe-0WAox", + "version": "4", + "developmentMode": true + }] + }, + "exceptionLogging": "STACKDRIVER" +} \ No newline at end of file diff --git a/src/timesheetAudit/clientsGroups.js b/src/timesheetAudit/clientsGroups.js new file mode 100644 index 0000000..7a248d9 --- /dev/null +++ b/src/timesheetAudit/clientsGroups.js @@ -0,0 +1,31 @@ +var onGoingClientsIds = onGoingCLients().map(function (group) { + return group.groupId +}); +function listAllGroups() { + var pageToken; + var page; + var onGoingGroups=[] ; + do { + page = AdminDirectory.Groups.list({ + domain: 'coderbunker.com', + maxResults: 150, + pageToken: pageToken + }); + var groups = page.groups; + if (groups) { + for (var i = 0; i < groups.length; i++) { + if (onGoingClientsIds !== undefined && onGoingClientsIds.indexOf(groups[i].id) !== -1) { + onGoingGroups.push(groups[i]) + } + ; + // Logger.log('%s (%s)', group.name, group.email) + } + } + // else { + // Logger.log('No groups found.') + // } + pageToken = page.nextPageToken; + } while (pageToken) + //Logger.log(onGoingGroups); + return onGoingGroups; +} \ No newline at end of file diff --git a/src/timesheetAudit/onGoingCLients.js b/src/timesheetAudit/onGoingCLients.js new file mode 100644 index 0000000..c76f75e --- /dev/null +++ b/src/timesheetAudit/onGoingCLients.js @@ -0,0 +1,48 @@ +function onGoingCLients() { + var onGoingClientsList=[]; + // get the current sheet in the active spreadSheet in this case {coderbunker opportunities account report} + var currentSpreadsheet = SpreadsheetApp.getActiveSpreadsheet(); + var currentSheet = currentSpreadsheet.getSheetByName('Accounts'); + + //status rows and colums values + var statusFirstRow = 1 ; + var statusLastRow = currentSheet.getLastRow()-1; + var statusFirstColumn = 1; + var statusLastColumn = currentSheet.getLastColumn() -1; + + // status Data range + var status = currentSheet.getRange(statusFirstRow, statusFirstColumn, statusLastRow, statusLastColumn); + var statusValues = status.getValues(); + + // iterrate on the status to pick only the status with: 'ongoing' + for (var row in status.getValues()){ + // assign the row of a client to a variable + var clientRow = statusValues[row]; + var name = clientRow[statusValues[0].indexOf('Client')]; + var email = clientRow[statusValues[0].indexOf('email')]; + var status = clientRow[statusValues[0].indexOf('Status')]; + var folderRootLink = clientRow[statusValues[0].indexOf('Folder')]; + var timesheetLink = clientRow[statusValues[0].indexOf('Timesheet')]; + var auditsheetLink = clientRow[statusValues[0].indexOf('Auditsheet')]; + var groupsId = clientRow[statusValues[0].indexOf('Group Id')]; + + // check is the status of the client is 'ongoing' + if (clientRow[statusValues[0].indexOf('Status')] ==='Ongoing'){ + //push a client object to onGoingClients with clients informations (clientName, CurrentStatus, timeSheetLink, auditsheetLink) + var clientInfo = { + clientName: name, + currentStatus : status, + folderLink : folderRootLink , + timeSheetLink : timesheetLink, + email : email, + auditLink : auditsheetLink, + groupId : groupsId + }; + //CREATE A CLIENT REPORT SPREADSHEET IN THE CLIENT FOLDER + onGoingClientsList.push( + clientInfo + ); + } + } + return onGoingClientsList; +} \ No newline at end of file diff --git a/src/timesheetAudit/timeSheetReport.js b/src/timesheetAudit/timeSheetReport.js new file mode 100644 index 0000000..19dceb6 --- /dev/null +++ b/src/timesheetAudit/timeSheetReport.js @@ -0,0 +1,209 @@ +function timeSheetReport(clientName){ + var clientGroup = listAllGroups().filter( + function (client) { + return client.id === clientName.groupId; + } + ) + var data = timesheetBasicInfo(clientName).data; + var header = timesheetBasicInfo(clientName).header; + // GET THE CURRENT CLIENT GOOGLE GROUP + var clientGoogleGroup = clientMailNGoogleGroup(clientName); + //var clientAuditsheet = SpreadsheetApp.openByUrl(clientName.auditLink).getSheetByName('Timesheet memebers') + + /* TIME SHEET AUDIT AND REPORT BEGIN */ +// ITERATE ALL THE ROW TO PICK APPROVED FREELANCERS AND UNKNOWN FREELANCER AND INCORRECT INFO IN THE JSON FILE + var statuses = {}; + for(var row in data){ + if (row >= 1){ + var thisRow = data[row]; + // BASIC INFORMATIONS ROW TO BE VERYFIED + var name = thisRow[header.indexOf('Full name')]; + var validity = thisRow[header.indexOf('Validity date')] ; + var rate = thisRow[header.indexOf('Rate')]; + //FREELANCER INFO IN THE JSON FILE + var freelancer = FreelancersInfo(name) + statuses[name]= freelancerStatus(freelancer, clientGoogleGroup, validity, rate) + } + }; + //EMAILS TO SEND WHEN THE AFTER TIMESHEET AUDIT. + //EMAIL SENT TO REPORT UNKNOWN USER IN THE JSON FILE + //'am.client@coderbunker.com' + var statusesKeys = Object.keys(statuses); + var UnknownUser = statusesKeys.filter(function(key){ return statuses[key] === 'UnknownUser'}); + var shouldNotBeInGoogleGroup = statusesKeys.filter(function(key){ return statuses[key] === 'shouldNotBeInGoogleGroup'}); + var shouldBeInGoogleGroup = statusesKeys.filter(function(key){ return statuses[key] ==='shouldBeInGoogleGroup'}); + var membersJsonfileLink = DriveApp.getFileById('1YgThK0YM8d_RiEc-IBPBBft0vMEeGQGg').getUrl() + if ( UnknownUser.length >= 1){ + GmailApp.sendEmail(['mohamed.semega@coderbunker.com'], + 'member.json file, information report', + 'TEST EMAIL'+ + '\nHello, '+ + '\n I would like to report, the following User: \n'+ + UnknownUser + + + '\n Please, follow these recommendations, to solve the issue: '+ + '\n 1. Go to: '+clientName.timeSheetLink + + '\n 2. Search the names mentioned above,'+ + '\n 3. Update the name to be an exact copy of the name(s) here: '+membersJsonfileLink+ + '\n Kind regards,' + ) + } + if(shouldNotBeInGoogleGroup.length >= 1){ + GmailApp.sendEmail(['mohamed.semega@coderbunker.com'], + clientName.clientName+' google group to be updated', + 'TEST EMAIL'+ + '\nHello,'+ + '\n I would like to report , the following members : \n' + + '\n 1. Go to the client google group: '+clientName.timeSheetLink+ + '\n 2. delete the emails of the names, mentioned here from the google group.'+ + shouldNotBeInGoogleGroup + +'>>\nKind regards,' + ) + } + if(shouldBeInGoogleGroup.length >= 1){ + GmailApp.sendEmail(['mohamed.semega@coderbunker.com'], + clientName.clientName+' google member update', + 'TEST EMAIL\nHello, \n'+ + 'I would like to report that the following person(s) \n '+ + shouldBeInGoogleGroup+ + '\n Please, follow these recommendations, to solve the issue: '+ + '\n 1. Go to the client google group: '+clientName.clientName+ + '\n 2. Add the email above in the google group; Or update the emails to the profil of the right person members.json object'+ + '\n 3. Save the changes'+ + '\n 4. The issue, will be solved after completion of the steps.'+ + '\nKind regards,') + } +} + +// FUCNTION TO TAKE OF THE TIMESHEET OF THE CLIENT +function timesheetBasicInfo(theClient){ + // BASIC INFORMAITONS OF THE CLIENT SHEET + var spreadsheetLink = theClient.timeSheetLink; + var sheetName = 'Team'; + var currentTeamTimeSheet = SpreadsheetApp.openByUrl(spreadsheetLink).getSheetByName(sheetName); + var sheetFirstRow = 1; + var sheetLastRow = currentTeamTimeSheet.getLastRow()-1; + var sheetFirstColumn = 1; + var sheetLastColumn = currentTeamTimeSheet.getLastColumn() -1; + // THE SHEET DATA RANGE TO PROCESS + var data = currentTeamTimeSheet.getRange(sheetFirstRow, sheetFirstColumn, sheetLastRow, sheetLastColumn).getValues(); + var headerRow = data[0]; + return { + sheetLink :spreadsheetLink, + sheetName :sheetName, + teamTimeSheet :currentTeamTimeSheet, + header :headerRow, + data :data, + } +} + +// FUNCTION TO PROCESS THE MAIL AND GOOGLE GROUP OF THE CLIENT +function clientMailNGoogleGroup(theClient){ + //ALL Groups + var allGroups = AdminDirectory.Groups.list({domain:"coderbunker.com"}).groups; + var clientEmail = allGroups.filter(function(e){ + if(theClient.groupId.trim() === e.id){ + return e + } + })[0]["email"]; + // client google group + return GroupsApp.getGroupByEmail(clientEmail); +} +// check the altemails in the client group +function altEmailCheck(altemail, group){ + var inGroup = false + var arrayOfAltEmails = altemail.trim().split(','); + for (var x in arrayOfAltEmails){ + if(group.hasUser(arrayOfAltEmails[x])){ + return inGroup = true + } + } + return inGroup +}; + +function freelancerStatus(freelancer, group, validity, rate){ + // CHECK ALL THE MEMBER VALIDITY AND EMAIL IN THE MEMBER.JSON FILE + if (freelancer !== undefined && freelancer.hasOwnProperty('email')){ + var mailcheck = group.hasUser(freelancer.email); + var userInTheGroup = mailcheck ? + mailcheck : ( + (freelancer.hasOwnProperty('altemails') )? + //CALLING THE ALEMAIL FUNCTION TO CHECK THE ALTEMAIL IN THE CLIENT GROUP + altEmailCheck(freelancer.altemails, group=group) : + false + ); + // CHECK IF THE FREELANCER HAS INVALID DATA AND IN THE GOOGLE GROUP + if(validity === '' || rate <=0 && userInTheGroup ){ + return 'shouldNotBeInGoogleGroup' + } + if (validity !== '' && rate > 0 && !userInTheGroup){ + return 'shouldBeInGoogleGroup' + }else{ + return 'UserInTheGroup' + } + } + /*else if ( !freelancer.hasOwnProperty('email') || (freelancer.altemails !== undefined && !freelancer.hasOwnProperty('altemails'))){ + return 'UnknownUserEmail' + }*/ + else{ + return 'UnknownUser' + } +} +/* TIMESHEET AUDIT AND REPORT END */ + +/* GOOGLE GROUP AUDIT AND REPORT BEGIN */ +function googleGroupReport(clientName){ + const clientGoogleGroupUsers = clientMailNGoogleGroup(clientName).getUsers(); + const freelancerTimeSheetNames = timesheetBasicInfo(clientName).data.map( + function(e){ + return e[0].toLowerCase() + } + ); + //RETRIEVE USERS INFO USING THE GROUP EMAILS + var usersJsonInfo =clientGoogleGroupUsers.map( + function (userEmail) { + return FreelancersInfo(userEmail) !== (undefined || null) ? FreelancersInfo(userEmail) : { + "userEmail" : userEmail, + "jsonStatus": FreelancersInfo(userEmail) + } + } + ); + // ALL THE USERS NAMES AVAILABLE WITH VALID INFO + var allValidUsers = usersJsonInfo.filter( + function(object){ + if (object !== undefined && (!(object.hasOwnProperty("jsonStatus")) || !(object.hasOwnProperty("userEmail")))){ + return object + } + } + ); + + //ALL THE VALID USERS NOT IN THE TIMESHEET + var userNotInTheTimesheet = allValidUsers.filter( + function(element){ + return freelancerTimeSheetNames.indexOf(element.fullname.toLowerCase().trim()) === -1; + } + ); + // ALL USERS WITH INVALID INFO + var usersWithInvalidInfo = usersJsonInfo.filter( + function(element){ + if ( element !== undefined && (element.hasOwnProperty("userEmail") && element.hasOwnProperty("jsonStatus"))){ + return element + } + } + ); + + if ( userNotInTheTimesheet.length >= 1){ + var UserNotInSheetNames = userNotInTheTimesheet.map(function (e){ return e.fullname, e.email}) + GmailApp.sendEmail(['mohamed.semega@coderbunker.com'], + clientName.clientName+'Timesheet Report', + 'TEST EMAIL'+ + '\nHello, '+ + '\n I would like to report, the following: \n"'+UserNotInSheetNames+ + '\n Please, follow these recommendations, to solve the issue: '+ + '\n 1. Go to: '+clientName.timeSheetLink+ + '\n 2. Add the name(s) mentioned here to the timesheet'+ + '\n 3. The name must be an exact copy of the one in the members.json file.'+ + '\n Kind regards,') + } +} +/* GOOGLE GROUP AUDIT AND REPORT END */ \ No newline at end of file