Need to generate 4x or 2x reports in Google Sheets using Javascript. 1 Data set. Trying not to loop through data 4 times

StackOverflow https://stackoverflow.com/questions/21649370

Question

So I'm pretty new to JavaScript and have only started learning it so I could use it with Google sheets. I'm currently working on a project to generates reports via script, as the amount of formula in the sheet was utterly killing it.

So here is the issue I face. I have a sheet with form responses. I'd like to report on those responses. The report I'm currently working on a script to populate the Hourly Break Down sheet.

There are 4 reports in that sheet (only 1 working so far) IN cell A28 you can choose a team name. In cell I33, you choose a date. Report is generated based on that. As you can possibly see, the other reports are quite similar above, with selections, but it will report on all data from last 31 days.

So I guess my question is this. I could copy and past my code 4 times and adjust it to generate each report on the page. But I'm thinking there is possibly a better way, with only 1 script and 1 loop of the data. But perhaps I'm wrong.

My original plan was to fill the top part of the report with one script and the bottom half with another.

Here is a link to the copy of the Sheet. It's been made read only, so I don't lose where I'm up to, please select File and Make a copy to edit. https://docs.google.com/spreadsheet/ccc?key=0Av0VvbH19xMTdG9EcU95Wk5BSGRqNTJDaW9fQmRCeUE&usp=drive_web#gid=125

Here is my code. After recent feedback on Code review, I've tried to annotate it as best I can.

All feedback is appreciated and welcomed :) Thank you!

function byHourByTeamByDate() {
  var emailColumn =1; // Column no with email in it
  var dateColumn = 2; // Column no with date in it
  var findColumn = 4; // Column no with values to count in it
  var teamColumn = 43; // Column no with team names in it
  var comFind = "Yes - Complete (going ahead)";  // A value to find
  var ansFind = "No Answer";                     // A value to find 
  var intFind = "No - Not interested in taking part"; // A value to find
  var cbFind = "Callback";                            // A value to find  
  var tolFind = "Temp Offline (If TOL do not continue)"; // A value to find
  var target = "Hourly Break Down";  // Name of target sheet, also has our date for report and team name.
  var ss = SpreadsheetApp.getActiveSpreadsheet(); 
  var sheet = ss.getSheetByName('Form Responses'); // Source Sheet.
  var values = sheet.getDataRange().getValues(); /* gets the values from Form responses */
  var targetSheet = ss.getSheetByName(target); /* loads our target sheet */
  var reportDate = new Date (targetSheet.getRange(33,9).getValue()); /* gets a date from target sheet, this tells us what date to report on */
  var teamNameByMonth = targetSheet.getRange(1, 1).getValue(); /* Get the team name for the whole months report */
  var teamNameByDate = targetSheet.getRange(28, 1).getValue(); /* Get the team name for the report for that date */

  var anHour = 3600000 ; /* An hour in milliseconsd, to help increment by an hour */

  var archive = [];
  var timeNames = ["12am","1am","2am","3am","4am","5am","6am","7am","8am","9am","10am","11am","12pm","1pm","2pm","3pm","4pm","5pm","6pm","7pm","8pm","9pm","10pm","11pm"];

  // The below vars are to enable to count up valuse found in findColumn, 5 different values to check for. These are subtotatls
  var completed = 0; 
  var noAnswer = 0; 
  var callBack = 0;
  var notInterested = 0;
  var tempOffline = 0;

 // These vars are to keep an overall total 
  var totCom = 0;
  var totAns = 0;
  var totCal = 0;
  var totNot = 0;
  var totTol = 0;

  var startGap = new Date().setTime(reportDate.getTime()); /* Convert our date found to milliseconds since epoch to give us a start time/date */
  var endGap = new Date().setTime(reportDate.getTime()+86400000); /* This adds a day to startGap, giving us a 1 day peroid. Var is un-used currently, but was looking to use it later*/

  /* the first for loop is meant to go through our timesName array, one by one */

  for(var j = 0; j < timeNames.length;j++ ){

  /* This second loop then goes through the data in form responses */
  for (var counter =  1; counter < values.length; counter++) {

    /* testDate converts our date values in form responses into a time/date in milliseconds */
    var testDate = new Date(values[counter][dateColumn -1]);


/*  below is a test to see if the date of vales are in the date/time range we are interested in  */
 if (testDate > startGap && testDate < startGap + anHour) {


   /* below checks to see if the team name matches the team name we've returned in teamNameByDate. If it doesn't, data is ignored */

    var team = values[counter][teamColumn -1];
   if(team != teamNameByDate)

 {
 continue
 }



 /* value is defined, so I don't have to repeatedly type, values[counter][findColumn -1] */

 var value = values[counter][findColumn -1]; 
/* below checks the findColumn in form resposnes, it should then add to subtotals and totals if matching items are found */


if (value == comFind) {
      completed++;
      totCom++;
  }

  if (value == ansFind) {
      noAnswer++;
      totAns++;
  }

  if (value == cbFind) {
      callBack++;
      totCal++;
  }

  if (value == intFind) {
      notInterested++;
      totNot++;
  }

  if (value == tolFind) {
      tempOffline++;
      totTol++;
}



}

}

  /* below we calculate some values for our report */

 var attempts = noAnswer+callBack+completed+notInterested+tempOffline;
 var success = completed/attempts*100;
 /* when outputting to report, I kept getting NaN on sucess sometimes, so added the following to fix it */

 if(!success)
 {success=0;}

  /* once we've gone through all data, for the date we want. We push every hours data to an array called archive */  
 archive.push([timeNames[j],attempts,completed,noAnswer,callBack,notInterested,tempOffline,success]);

 /* subtotals are reset below. We also add an hour on to startGap to find next hours worth of data */
  completed = 0;
  noAnswer = 0;
  callBack = 0;
  notInterested = 0;
  tempOffline = 0;
  startGap = startGap+anHour;




}
// The intention with the code below is to add up the values in the morning.
// As I don't want 12am to 8am on the final report. But adding them together JUST incase there are 1 or two entries.
// I know it's the first 10 entries of Archive that need added, hence the loop.  
 var mornAtt = 0;
 var mornCom = 0;
 var mornAns = 0;
 var mornCal = 0;
 var mornNot = 0;
 var mornTol = 0;
 var mornTime = "9am"; 
 for(n=0;n<10;n++)
 {


 mornAtt = mornAtt + archive[n][1];
 mornCom = mornCom + archive[n][2];
 mornAns = mornAns + archive[n][3];
 mornCal = mornCal + archive[n][4];
 mornNot = mornNot + archive[n][5];
 mornTol = mornTol + archive[n][6];


} 
  //Remove 12am to 9am from acrhive below.
 archive.splice(0,10);

 var mornSuccess = mornCom/mornAtt;
 if(!mornSuccess)
{mornSuccess=0;}
 // Adds newly created 9am values to archive. 
 archive.unshift([mornTime,mornAtt,mornCom,mornAns,mornCal,mornNot,mornTol,mornSuccess]);


  // This is very similar to the last entries. We don't want 8pm to 11pm on the report.
 // So I'm adding those entries below. Removing 8 -11 pm from archive then inserting new data to archive as a single array.

 var eveAtt = 0;
 var eveCom = 0;
 var eveAns = 0;
 var eveCal = 0;
 var eveNot = 0;
 var eveTol = 0;
 var eveTime = "7pm"; 


 for(i = archive.length -1; i > 10; i--)
 {


 eveAtt = eveAtt + archive[i][1];
 eveCom = eveCom + archive[i][2];
 eveAns = eveAns + archive[i][3];
 eveCal = eveCal + archive[i][4];
 eveNot = eveNot + archive[i][5];
 eveTol = eveTol + archive[i][6];


} 



// 8pm to 11pm entries removed
archive.splice(10,5);

var eveSuccess = eveCom/eveAtt;
if(!eveSuccess)
{eveSuccess=0;}

// new entries added to archive
archive.push([eveTime,eveAtt,eveCom,eveAns,eveCal,eveNot,eveTol,eveSuccess]);

// The archive is written to the report page  
targetSheet.getRange(30,1).offset(0, 0, archive.length, archive[0].length).setValues(archive);

// below is totals for use in our footer/totals rows

var totAtt = totCom+totAns+totCal+totNot+totTol;
var totSuc = totCom/totAtt*100;
if(!totSuc){
totSuc=0;
}
 // below our totals/footers are defined 
var footers =[["Totals",totAtt,totCom,totAns,totCal,totNot,totTol,totSuc]];  

  // Below our totals are added to the sheet

  targetSheet.getRange(41,1,1,8).setValues(footers);
  Browser.msgBox("Report Generated","Hamster is knackered, but report is finished",Browser.Buttons.OK);

}
Was it helpful?

Solution

Instead of the conditional "gate" check:

   if(team != teamNameByDate)

{ continue }

maybe something like this: define 4 sets of team-specific arrays and one set of temp assignment arrays like in this 2 case-1 array example:

>var a=[1,2,3,4]
>var b=["a","b","c","d"]
>var c=a;
>c.push(5)
5
>a
[1, 2, 3, 4, 5]
>c
[1, 2, 3, 4, 5]
>c=b;
["a", "b", "c", "d"]
>a
[1, 2, 3, 4, 5]
>c
["a", "b", "c", "d"]
>c.push("e")
5
>c
["a", "b", "c", "d", "e"]
>b
["a", "b", "c", "d", "e"]
>a
[1, 2, 3, 4, 5]

Then as you loop through the data, assign the working array to one of the team arrays based on the value of "team"

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top