diff --git a/actionTrackerSheet.js b/actionTrackerSheet.js index f3f547b..102dfda 100644 --- a/actionTrackerSheet.js +++ b/actionTrackerSheet.js @@ -1,10 +1,17 @@ /* Purpose: 1. Search agendas for action items to be completed and populate in the Action Tracking Google Sheet. 2. Push status updates from from the Action Tracking Google sheet to the Action source agendas as changed. +/* Purpose: +1. Search agendas for action items to be completed and populate in the Action Tracking Google Sheet. +2. Push status updates from from the Action Tracking Google sheet to the Action source agendas as changed. +Future development: +Preserve links in task items from the source documents. Will first require the same development in the inDocActionItems script, as the links are lost in that action first. Future development: Preserve links in task items from the source documents. Will first require the same development in the inDocActionItems script, as the links are lost in that action first. +To note: +This script is developed as a Google Apps Script container script: i.e. a script that is bound to a specific file, such as a Google Sheets, Google Docs, or Google Forms file. This container script acts as the file's custom script, allowing users to extend the functionality of the file by adding custom functions, triggers, and menu items to enhance its behavior and automation. To note: This script is developed as a Google Apps Script container script: i.e. a script that is bound to a specific file, such as a Google Sheets, Google Docs, or Google Forms file. This container script acts as the file's custom script, allowing users to extend the functionality of the file by adding custom functions, triggers, and menu items to enhance its behavior and automation. @@ -107,8 +114,60 @@ function onOpen() { .addItem('Push Status Updates to Source Document','updateStatus') >>>>>>> b746d3712728e7b81086aac526d2a6c48e28b83d .addToUi(); - // to open workbook with up-to-date All Open (Sort Only - Do not Edit) tab - copyDataToAllOpenSheet(); +} + +// Global variables +var spreadsheetId = '13xgmbfP8X8lu9tlD_cHVCF9RmQeKvToqtbiDSfp_Nfg'; // Testing ALL Action Tracking Sheet +var folderIds = { + MO: '1SRIUs7CUEdGUw0r1PI52e0OJpfXYN0z8', + SEP: '1Cw_sdH_IleGbtW1mVoWnJ0yqoyzr4Oe0', + DevSeed: '1Bvj1b1u2LGwjW5fStQaLRpZX5pmtjHSj' +}; + +///////////////////Pull/////////////////////////////////////////////// + +// Function to pull actions for all folders +function pullActionsForAllFolders() { + logExecutionTime(function() { + pullActionsForFolder('MO'); + pullActionsForFolder('SEP'); + pullActionsForFolder('DevSeed'); + }, 'pullActionsForAllFolders'); +} + +// Function to pull actions for MO folder +function pullActionsForFolderMO() { + logExecutionTime(function() { + pullActionsForFolder('MO'); + }, 'pullActionsForFolderMO'); +} + +// Function to pull actions for SEP folder +function pullActionsForFolderSEP() { + logExecutionTime(function() { + pullActionsForFolder('SEP'); + }, 'pullActionsForFolderSEP'); +} + +// Function to pull actions for DevSeed folder +function pullActionsForFolderDevSeed() { + logExecutionTime(function() { + pullActionsForFolder('DevSeed'); + }, 'pullActionsForFolderDevSeed'); +} + +// Helper function to pull actions from a specific folder +function pullActionsForFolder(folderName) { + var folderId = folderIds[folderName]; + var tablePullSheetName = folderName; + + Logger.log('Step 1: Pulling actions from documents...'); + var actions = pullActionsFromDocuments(folderId); + Logger.log('Step 1: Actions retrieved:', actions); + + Logger.log('Step 2: Populating sheet with actions...'); + populateSheetWithActions(spreadsheetId, tablePullSheetName, actions); + Logger.log('Step 2: Sheet populated with actions.'); } // Global variables @@ -213,6 +272,7 @@ function pullActionsFromDocuments(folderId) { return allActions; } +// Pull helper function: Find the second table with qualifying headers // Pull helper function: Find the second table with qualifying headers function findSecondTable(tables) { for (var i = 0; i < tables.length; i++) { @@ -235,6 +295,7 @@ function findSecondTable(tables) { return null; } +// Pull helper function: Convert table to 2D array for convenience and flexibility in data processing // Pull helper function: Convert table to 2D array for convenience and flexibility in data processing function tableTo2DArray(table) { var numRows = table.getNumRows(); @@ -253,6 +314,7 @@ function tableTo2DArray(table) { return data; } +// Pull helper function: Populate the Sheet with actions // Pull helper function: Populate the Sheet with actions function populateSheetWithActions(spreadsheetId, sheetName, actions) { var spreadsheet = SpreadsheetApp.openById(spreadsheetId); @@ -263,6 +325,8 @@ function populateSheetWithActions(spreadsheetId, sheetName, actions) { } else { // Clear only the contents of columns A to E sheet.getRange(1, 1, sheet.getMaxRows(), 5).clearContent(); + // Clear only the contents of columns A to E + sheet.getRange(1, 1, sheet.getMaxRows(), 5).clearContent(); } var headerRow = ['Action Source', 'Status', 'Assigned to', 'Task']; @@ -272,6 +336,7 @@ function populateSheetWithActions(spreadsheetId, sheetName, actions) { var numCols = headerRow.length; if (numRows > 0 && numCols > 0) { + // Set values starting from cell A1 // Set values starting from cell A1 var range = sheet.getRange(1, 1, numRows, numCols); range.setValues(actions); @@ -288,6 +353,7 @@ function MOPopulate() { Logger.log('Step 1: Pulling actions from documents...'); var actions = pullActionsFromDocuments(folderIds.MO); + var actions = pullActionsFromDocuments(folderIds.MO); Logger.log('Step 1: Actions retrieved:', actions); Logger.log('Step 2: Populating sheet with actions...'); @@ -305,7 +371,116 @@ pullActionsForAllFolders: success 11:02:56 AM pullActionsForAllFolders executi */ ////////////////// Push //////////////////////////////////////////////////////// +////////////////// Pull Testing Log ////////////////////////////////////////// + +/* +pullActionsForFolderMO: success 2024-01-11 10:43:24 AM pullActionsForFolderMO execution time: 0 minutes, 12.737 seconds +pullActionsForFolderSEP: success 2024-01-10 10:46:01 AM pullActionsForFolderSEP execution time: 0 minutes, 10.042 seconds +pullActionsForFolderDevSeed: success 10:59:36 AM pullActionsForFolderDevSeed execution time: 0 minutes, 9.670 seconds +pullActionsForAllFolders: success 11:02:56 AM pullActionsForAllFolders execution time: 0 minutes, 37.867 seconds +*/ + +////////////////// Push //////////////////////////////////////////////////////// + +// Function to call the primary function on all named tabs +function pushActionsFromAllTabs() { + logExecutionTime(function() { + updateStatusOnTab("MO"); + updateStatusOnTab("SEP"); + updateStatusOnTab("DevSeed"); + }, 'pushActionsFromAllTabs'); +} + +// Function to call the primary function on the "MO" tab +function pushActionsFromTabMO() { + logExecutionTime(function() { + updateStatusOnTab("MO"); + }, 'pushActionsFromTabMO'); +} + +// Function to call the primary function on the "SEP" tab +function pushActionsFromTabSEP() { + logExecutionTime(function() { + updateStatusOnTab("SEP"); + }, 'pushActionsFromTabSEP'); +} +// Function to call the primary function on the "DevSeed" tab +function pushActionsFromTabDevSeed() { + logExecutionTime(function() { + updateStatusOnTab("DevSeed"); + }, 'pushActionsFromTabDevSeed'); +} + +function updateStatusOnTab(tabName) { + var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(tabName); + var range = sheet.getRange("A:G"); // Assuming the URL is in Column G + var values = range.getValues(); + + // Loop through each row of the data + for (var i = 1; i < values.length; i++) { + var actionSourceUrl = values[i][6]; // Column G (assuming the URL is in Column G) + var status = values[i][1]; // Column B + var assignedTo = values[i][2]; // Column C + var task = values[i][3]; // Column D + + // Check if the row is empty or if it's the header row + if (actionSourceUrl || status || assignedTo || task) { + // Log actionSourceUrl for investigation + console.log("Row:", i + 1, "actionSourceUrl:", actionSourceUrl); + + // Call the function to update status in the source document + updateStatusInSourceDoc(actionSourceUrl, status, task); + } + } +} + +function updateStatusInSourceDoc(actionSource, status, task) { + // Column G now contains the extracted URL + var actionSourceUrl = actionSource; // No need for extractUrlFromHyperlink + + if (!actionSourceUrl) { + console.error("Invalid URL in Column A:", actionSource); + return; + } + + try { + console.log("Attempting to open document with URL:", actionSourceUrl); + var sourceDoc = DocumentApp.openByUrl(actionSourceUrl); + } catch (error) { + console.error("Error opening document by URL:", error); + return; + } + // Get the body of the document + var body = sourceDoc.getBody(); + + // Find the table with the specified headings + var table = findTableByHeadings(body, ["Status", "Action"]); + + if (table) { + // Find the row with the matching task in the "Action" column + var rowIndex = findRowIndexByColumnValue(table, "Action", task); + + if (rowIndex !== -1) { + // Update the "Status" column with the status from the sheet + table.getCell(rowIndex, getColumnIndex(table, "Status")).setText(status); + } + } +} + +function findTableByHeadings(body, headings) { + // Function to find a table in the document with specified headings + var tables = body.getTables(); + + for (var i = 0; i < tables.length; i++) { + var table = tables[i]; + var headerRow = table.getRow(0); + + // Check if the table has the specified headings + if (headings.every(function (heading) { + return headerRow.getText().indexOf(heading) !== -1; + })) { + return table; // Function to call the primary function on all named tabs function pushActionsFromAllTabs() { logExecutionTime(function() { @@ -408,6 +583,7 @@ function findTableByHeadings(body, headings) { } } + return null; }