Advertisement
freephile

Office Automation - Copy spreadsheet data

Sep 9th, 2012
281
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. /**
  2.  * See the documentation at https://developers.google.com/apps-script/storing_data_spreadsheets
  3.  */
  4.  
  5.  
  6. /*
  7.  * Our main handler
  8.  * The onEdit trigger is one of three built-in triggers
  9.  * This function, if defined, is called automatically
  10.  * @see https://developers.google.com/apps-script/understanding_triggers
  11.  */
  12. function onEdit(event) {
  13.   var ss = event.source.getActiveSheet();
  14.   var r = event.source.getActiveRange();
  15.   r.setComment("Last modified: " + (new Date()));
  16.   copyPhoneList();
  17.   zebraStripe("Read Only");
  18.   protectSheet('Read Only');
  19. //   createOfficeSheets(); // don't do this every edit
  20. }
  21.  
  22. // The onOpen function is executed automatically every time a Spreadsheet is opened
  23. function onOpen(e) {
  24.  
  25.   var ss = SpreadsheetApp.getActiveSpreadsheet();
  26.   var menuEntries = [];
  27.   // When the user clicks on "addMenuExample" then "Menu Entry 1", the function function1 is executed.
  28.   menuEntries.push({name: "Generate Office Sheets", functionName: "createOfficeSheets"});
  29.   ss.addMenu("Internal Tools", menuEntries);
  30.  
  31.   createOfficeSheets();  
  32. }  
  33.  
  34.  
  35. /*
  36.  * A function to add protection to a sheet so that it's not
  37.  * inadvertently modified
  38.  */
  39. function protectSheet(sheetName) {
  40.   if (typeof(sheetName) == "undefined") {
  41.     var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
  42.   } else {
  43.     var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
  44.   }
  45.  
  46.   var permissions = sheet.getSheetProtection();
  47.   var isProtected = permissions.isProtected();
  48.   if (isProtected) {
  49.     // Browser.msgBox('The sheet ' + sheetName + ' has been protected');
  50.   } else {
  51.     // Browser.msgBox('The perission check shows ' + permissions );
  52.     permissions.addUser('greg.rundlett@gmail.com');
  53.     permissions.addUser('editors@example.com');
  54.     permissions.addUser('joe.boss@example.com');
  55.     permissions.addUser('mary.manager@example.com');
  56.     permissions.setProtected(true);
  57.     sheet.setSheetProtection(permissions);
  58.   }
  59. }
  60.  
  61. /**
  62.  * Our main function - responsible for copying data from our master data set to another copy that
  63.  * is read-only (due to the sheet protection added to it).
  64.  */
  65. function copyPhoneList() {
  66.   var hasFirstName = false;
  67.   var ss = SpreadsheetApp.getActiveSpreadsheet();
  68.  
  69.   // var sheet = ss.getSheetByName("Master");
  70.   // var data = sheet.getDataRange().getValues(); // the whole sheet
  71.  
  72.   var data = ss.getRangeByName("masterPhoneListData").getValues();  // the named range
  73.   var range = ss.getRangeByName("masterPhoneListData");
  74.   var numColumns = range.getEndColumn() - range.getColumn() + 1;
  75.   //var numColumns = data[10].length; // how many columns are in each row
  76.   var phoneList = new Array();
  77.    
  78.  
  79.   // then we'll iterate through our source data and push recordDatas over to the target for each non-empty row
  80.   for (i=0; i<data.length; i++) {       // we've already used a named data range to skip it's header
  81.     hasFirstName = !isCellEmpty(data[i][4]);    
  82.     if (hasFirstName) {                 // if there is something in firstname, then get the record
  83.       var record = new Array();
  84.       for (j=0; j<numColumns; j++) {
  85.         // we want to explicitly grab only columns a,b,c,d,e,g or 0,1,2,3,4,6
  86.         switch(j) {
  87.           case 0: // DID (Direct Inward Dial)
  88.           case 1: // Ext
  89.           case 2: // Office
  90.           case 3: // First Name
  91.           case 4: // Last Name
  92.           case 6: // Cell Phone
  93.             record.push(data[i][j]);
  94.             break;
  95.            
  96.           default:
  97.             break;
  98.          
  99.         }
  100.       }
  101.  
  102.       // now add each record to the data we're compiling
  103.       phoneList.push(record);
  104.     }
  105.   }
  106.  
  107.   // now push it all over to the target sheet
  108.   var target = ss.getSheetByName("Read Only");
  109.   target.clearContents(); // empty out our target sheet, preserving the formatting
  110.  
  111.   // first we'll create a heading row
  112.   var heading = ['Direct', 'Ext', 'Office', 'First Name', 'Last Name', 'Cell Phone'];
  113.   //target.appendRow(heading); // put the first heading row into our target sheet
  114.  
  115.   var headersRange = target.getRange(1, 1, 1, heading.length);
  116.   headersRange.setValues([heading]);
  117.  
  118.  
  119.   // we'd like to have sorted data, by First Name, and if those are equal, then by Last Name
  120.   // JavaScript arrays can be sorted by using a callback function supplied as the argument to the sort() method call
  121.   // http://www.sitepoint.com/sophisticated-sorting-in-javascript/
  122.   phoneList.sort(function(a, b) {
  123.     if(a[3].toLowerCase() === b[3].toLowerCase()) {
  124.       var x = a[4].toLowerCase(), y = b[4].toLowerCase();
  125.       return x < y ? -1 : x > y ? 1 : 0;
  126.     } else {
  127.       var x = a[3].toLowerCase(), y = b[3].toLowerCase();
  128.       return x < y ? -1 : x > y ? 1 : 0;
  129.     }
  130.   });
  131.  
  132.   // All done, put the data into place
  133.   target.getRange(2, 1, phoneList.length, phoneList[0].length).setValues(phoneList);
  134. }
  135.  
  136.  
  137.  
  138. /**
  139.  * Functionality based on the Google example code to populate a bunch of sheets based on
  140.  * the "office" value in our master data set (The example breaks down a list of employees by department)
  141.  */
  142.  
  143.  
  144. // This is where the data used will be retrieved from:
  145. var DATA_SPREADSHEET_ID = "0AqwQ0jPilu4IdGNqMzNXSy15ZWQ3c0ZFMkZja2RuS2c";
  146.  
  147. function createOfficeSheets() {
  148.   var ss = SpreadsheetApp.getActiveSpreadsheet();
  149.   var sheet = ss.getSheets()[0];
  150.  
  151.   var dataSs = SpreadsheetApp.openById(DATA_SPREADSHEET_ID);
  152.   // var dataSheet = dataSs.getSheets()[0];
  153.   var dataSheet = ss.getSheetByName("Read Only");
  154.  
  155.   // Fetch all the data
  156.   var data = getRowsData(dataSheet);
  157.  
  158.   // This is the data we want to display
  159.   var columnNames = ['Direct', 'Ext', 'Office', 'First Name', 'Last Name', 'Cell Phone'];
  160.  
  161.   // Index data by office name
  162.   var dataByOffice = {};
  163.   var offices = [];
  164.   for (var i = 0; i < data.length; ++i) {
  165.     var rowData = data[i];
  166.     if (!dataByOffice[rowData.office]) {
  167.       dataByOffice[rowData.office] = [];
  168.       offices.push(rowData.office);
  169.     }
  170.     dataByOffice[rowData.office].push(rowData);
  171.   }
  172.  
  173.   offices.sort();
  174.   var headerBackgroundColor = dataSheet.getRange(1, 1).getBackgroundColor();
  175.   for (var i = 0; i < offices.length; ++i) {
  176.     var sheet = ss.getSheetByName(offices[i]) ||
  177.       ss.insertSheet(offices[i], ss.getSheets().length);
  178.     sheet.clear();
  179.     var headersRange = sheet.getRange(1, 1, 1, columnNames.length);
  180.     headersRange.setValues([columnNames]);
  181.     headersRange.setBackgroundColor(headerBackgroundColor);
  182.     setRowsData(sheet, dataByOffice[offices[i]]);
  183.   }
  184. }
  185.  
  186. /*************************************************************************************  
  187.  * Code reused from Reading Spreadsheet Data using JavaScript Objects               **
  188.  * https://developers.google.com/apps-script/storing_data_spreadsheets#reading-code **
  189.  *************************************************************************************/
  190.  
  191.  
  192. // setRowsData fills in one row of data per object defined in the objects Array.
  193. // For every Column, it checks if data objects define a value for it.
  194. // Arguments:
  195. //   - sheet: the Sheet Object where the data will be written
  196. //   - objects: an Array of Objects, each of which contains data for a row
  197. //   - optHeadersRange: a Range of cells where the column headers are defined. This
  198. //     defaults to the entire first row in sheet.
  199. //   - optFirstDataRowIndex: index of the first row where data should be written. This
  200. //     defaults to the row immediately below the headers.
  201. function setRowsData(sheet, objects, optHeadersRange, optFirstDataRowIndex) {
  202.   var headersRange = optHeadersRange || sheet.getRange(1, 1, 1, sheet.getMaxColumns());
  203.   var firstDataRowIndex = optFirstDataRowIndex || headersRange.getRowIndex() + 1;
  204.   var headers = normalizeHeaders(headersRange.getValues()[0]);
  205.  
  206.   var data = [];
  207.   for (var i = 0; i < objects.length; ++i) {
  208.     var values = []
  209.     for (j = 0; j < headers.length; ++j) {
  210.       var header = headers[j];
  211.       values.push(header.length > 0 && objects[i][header] ? objects[i][header] : "");
  212.     }
  213.     data.push(values);
  214.   }
  215.  
  216.   var destinationRange = sheet.getRange(firstDataRowIndex, headersRange.getColumnIndex(),
  217.                                         objects.length, headers.length);
  218.   destinationRange.setValues(data);
  219. }
  220.  
  221.  
  222.  
  223. // getRowsData iterates row by row in the input range and returns an array of objects.
  224. // Each object contains all the data for a given row, indexed by its normalized column name.
  225. // Arguments:
  226. //   - sheet: the sheet object that contains the data to be processed
  227. //   - range: the exact range of cells where the data is stored
  228. //       This argument is optional and it defaults to all the cells except those in the first row
  229. //       or all the cells below columnHeadersRowIndex (if defined).
  230. //   - columnHeadersRowIndex: specifies the row number where the column names are stored.
  231. //       This argument is optional and it defaults to the row immediately above range;
  232. // Returns an Array of objects.
  233. function getRowsData(sheet, range, columnHeadersRowIndex) {
  234.   var headersIndex = columnHeadersRowIndex || range ? range.getRowIndex() - 1 : 1;
  235.   var dataRange = range ||
  236.     sheet.getRange(headersIndex + 1, 1, sheet.getMaxRows() - headersIndex, sheet.getMaxColumns());
  237.   var numColumns = dataRange.getEndColumn() - dataRange.getColumn() + 1;
  238.   var headersRange = sheet.getRange(headersIndex, dataRange.getColumn(), 1, numColumns);
  239.   var headers = headersRange.getValues()[0];
  240.   return getObjects(dataRange.getValues(), normalizeHeaders(headers));
  241. }
  242.  
  243. // For every row of data in data, generates an object that contains the data. Names of
  244. // object fields are defined in keys.
  245. // Arguments:
  246. //   - data: JavaScript 2d array
  247. //   - keys: Array of Strings that define the property names for the objects to create
  248. function getObjects(data, keys) {
  249.   var objects = [];
  250.   for (var i = 0; i < data.length; ++i) {
  251.     var object = {};
  252.     var hasData = false;
  253.     for (var j = 0; j < data[i].length; ++j) {
  254.       var cellData = data[i][j];
  255.       if (isCellEmpty(cellData)) {
  256.         continue;
  257.       }
  258.       object[keys[j]] = cellData;
  259.       hasData = true;
  260.     }
  261.     if (hasData) {
  262.       objects.push(object);
  263.     }
  264.   }
  265.   return objects;
  266. }
  267.  
  268. // Returns an Array of normalized Strings.
  269. // Empty Strings are returned for all Strings that could not be successfully normalized.
  270. // Arguments:
  271. //   - headers: Array of Strings to normalize
  272. function normalizeHeaders(headers) {
  273.   var keys = [];
  274.   for (var i = 0; i < headers.length; ++i) {
  275.     keys.push(normalizeHeader(headers[i]));
  276.   }
  277.   return keys;
  278. }
  279.  
  280. // Normalizes a string, by removing all alphanumeric characters and using mixed case
  281. // to separate words. The output will always start with a lower case letter.
  282. // This function is designed to produce JavaScript object property names.
  283. // Arguments:
  284. //   - header: string to normalize
  285. // Examples:
  286. //   "First Name" -> "firstName"
  287. //   "Market Cap (millions) -> "marketCapMillions
  288. //   "1 number at the beginning is ignored" -> "numberAtTheBeginningIsIgnored"
  289. function normalizeHeader(header) {
  290.   var key = "";
  291.   var upperCase = false;
  292.   for (var i = 0; i < header.length; ++i) {
  293.     var letter = header[i];
  294.     if (letter == " " && key.length > 0) {
  295.       upperCase = true;
  296.       continue;
  297.     }
  298.     if (!isAlnum(letter)) {
  299.       continue;
  300.     }
  301.     if (key.length == 0 && isDigit(letter)) {
  302.       continue; // first character must be a letter
  303.     }
  304.     if (upperCase) {
  305.       upperCase = false;
  306.       key += letter.toUpperCase();
  307.     } else {
  308.       key += letter.toLowerCase();
  309.     }
  310.   }
  311.   return key;
  312. }
  313.  
  314. // Returns true if the cell where cellData was read from is empty.
  315. // Arguments:
  316. //   - cellData: string
  317. function isCellEmpty(cellData) {
  318.   return typeof(cellData) == "string" && cellData == "";
  319. }
  320.  
  321. // Returns true if the character char is alphabetical, false otherwise.
  322. function isAlnum(char) {
  323.   return char >= 'A' && char <= 'Z' ||
  324.     char >= 'a' && char <= 'z' ||
  325.     isDigit(char);
  326. }
  327.  
  328. // Returns true if the character char is a digit, false otherwise.
  329. function isDigit(char) {
  330.   return char >= '0' && char <= '9';
  331. }
  332.  
  333. // The code below will get the number of rows in sheet 0
  334. function getRowsCount() {
  335.   var ss = SpreadsheetApp.getActiveSpreadsheet();
  336.   var sheet = ss.getSheets()[0];
  337.   Browser.msgBox(sheet.getMaxRows());
  338. }
  339.  
  340.  
  341. // A function to alternately stripe a spreadsheet
  342. // for better legibility
  343. function zebraStripe(sheetName) {
  344.   var i, thisRow;
  345.  
  346.   if (typeof(sheetName) == "undefined") {
  347.     var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0];
  348.   } else {
  349.     var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
  350.   }
  351.  
  352.   var lastRow = sheet.getLastRow();
  353.   var lastCol = sheet.getLastColumn();
  354.   var aEven = "#e0f1f5";
  355.   var aOdd = "white";
  356.  
  357.   for (i = 1; i <= lastRow; i++) {
  358.     thisRow = sheet.getRange(i, 1, 1, lastCol);
  359.     thisRow.setBackgroundColor((i % 2 == 0)?aEven:aOdd);
  360.   }
  361. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement