0
votes

I created a spreadsheet for reporting students attendance that contains 8 sheets (each sheet named as a subject code). After completing a particular class, I go to the specific sheet (subject) and select all the rows of that particular date and press the button AfterClass-->Process Data (sorting, removing duplicates and protecting) using Google Apps Script/Macros. All working fine.

Now I created a DASHBOARD and I want that a teacher can do everything from the dashboard rather than going to individual sheet (subject). S/he can give two inputs - subject (sheetname) and Date from the Dashboard and then automatically process these specific dataset of that Sheet (Not all data of the sheet). Please note that date is in Column A and subject-code in Column F. The code I wrote as follows:

  function AfterClass() {
      var spreadsheet = SpreadsheetApp.getActive();
  //Sorting and removing duplicates
  var height = spreadsheet.getActiveSheet().getActiveRange().getHeight();
  spreadsheet.getCurrentCell().offset(0, 0, height, 6).activate()
  .sort({column: spreadsheet.getActiveRange().getColumn() + 2, ascending: true});
  spreadsheet.getActiveRange().removeDuplicates([spreadsheet.getActiveRange().getColumn() + 2]).activate();

  //Protecting data finally
  //var lastRow = spreadsheet.getLastRow();
  var timeZone = Session.getScriptTimeZone();
  var stringDate = Utilities.formatDate(new Date(), timeZone, 'dd/MM/yy HH:mm');
  var me = Session.getEffectiveUser();
  var description = 'Protected on ' + stringDate + ' by ' + me;
  var protection = SpreadsheetApp.getActiveSheet().getActiveRange().protect().setDescription(description);
  //protection.setDomainEdit(false);   
  protection.addEditor(me);
  protection.removeEditors(protection.getEditors());
  if (protection.canDomainEdit()) {
    protection.setDomainEdit(false);
  }      

//Removing blank spacess in between data
      var sheet = SpreadsheetApp.getActiveSheet();
      var rows = sheet.getDataRange();
      var numRows = rows.getNumRows();
      var values = rows.getValues();
      var rowsDeleted = 0;
      for (var i = 0; i <= numRows - 1; i++) {
        var row = values[i];
        if (row[1] == '') {
          sheet.deleteRow((parseInt(i)+1) - rowsDeleted);
          rowsDeleted++;
        }
      }   

     //For Double periods in a class
          //var ss = SpreadsheetApp.getActiveSpreadsheet()
          //var database = SpreadsheetApp.openById("xxx");
          //var source = ss.getSheetByName('yyy');
          var dataToCopyRng = SpreadsheetApp.getActiveSheet().getActiveRange(); //Gets range object of all data on source sheet
          var dataToCopy = dataToCopyRng.getValues(); //Gets the values of the source range in a 2 dimensional array
          var copyToSheet = SpreadsheetApp.getActiveSheet();
          var copyData = copyToSheet.getRange(copyToSheet.getLastRow()+1,1,dataToCopy.length,dataToCopy[0].length).setValues(dataToCopy); 

      //Calculate class attendance and signed
      var height2 = spreadsheet.getActiveSheet().getActiveRange().getHeight();   
      SpreadsheetApp.getActiveSheet().getCurrentCell().offset(2*height2,1).activate();
      SpreadsheetApp.getActiveSheet().getCurrentCell().setRichTextValue(SpreadsheetApp.newRichTextValue()
      .setText(height2 + ' Students, SIGNED')
      .setTextStyle(0, 12, SpreadsheetApp.newTextStyle()
      .setBold(true)
      .build())
      .build());
      spreadsheet.getCurrentCell().offset(0, -1, 1, 6).activate();
      spreadsheet.getActiveRangeList().setBackground('#e6b8af');
      //.setBackground('#d9d9d9')            

    }    

  [dashboard][1]


  [1]: https://i.stack.imgur.com/cMtHC.png
1
What have you tried so far to implement this feature yourself and where did you get stuck? Please provide your code.ziganotschka
Please check the codes in the post (edited). I am using StackOverflow for the first time. Note that each value in subject-code column is the sheet name (e.g. MATH4101), which I created using a trigger. Each time I add a row of data, it adds subject-code based on the sheet name. It is useful for visualisation in the Master sheet that combines all the sheets' data in one place. The data is coming from an android attendance app. If this problem is solved, it will be great help for me. Thanks in advance.Aktaruzzaman Liton
So the information in Dashboard contains only the subject and date? Or also the additional data that were visible in the screenshot you provided before? And do you mean by "process these specific dataset of that Sheet "? Do you want to copy data from the Dashboard sheet to the correct subject sheet (if so, how do you want to do so - when the teacher runs a custom function? And for which row? Or for the whole sheet?).? Or do you want instead to run function AfterClass for the correct subject sheet? A more detailed explanation would help to understand the issue better.ziganotschka
Dashboard is just a blank sheet for few visualizations. There are eight sheets (subjects) and these combined into one Master sheet and Dashboard is showing visualization from Master sheet. Only Dashboard is available to teachers, not any other subject sheets. In Dashboard there will be a menu After Class and 2-3 submenus - (i) Select the subject code (dropdown list of sheets)? (ii) Enter a date? Now when I enter a date and press OK, it will execute the AfterClass() function for the correct whole sheet and will select the correct range of data (all 6 COLs) based on input & process on that sheetAktaruzzaman Liton
So after the function AfterClass() is run you want to pull the data from the specified sheet and date into dashboard? And once the function is run again (with new subject code/date) youw ant to clear Dashboard and populate it with the new data?ziganotschka

1 Answers

0
votes

How to run your script from Dashboard after selecting the specified sheet and time

  • Modify your function in a way that it takes input from the cells A2 and C2 and D2 from Dashboard
  • Replace all instances of getActiveSheet() through getSheetByName(name), whereby name is your input from A1
  • Replace all instances of getActiveRange() through sheet.getRange(), whereby you define the range as as subrange as defined by the dates you retrieved
  • In order to find the first occurrence of the start date and the last occurrence of the end date - use the methods indexOf() and lastIndexOf()
  • Make sure that your date notation in Dashboard is the same as in the sheets for correct functionality

Sample:

function onOpen() {
  var ui = SpreadsheetApp.getUi();
  ui.createMenu('After Class')
  .addItem('Process Data', 'AfterClass')
  .addToUi();
}

function AfterClass() {
  var spreadsheet = SpreadsheetApp.getActive();
  var dashboard = spreadsheet.getSheetByName("Dashboard");
  var sheetName = dashboard.getRange("A2").getValue();
  //retrieve the start date to use as desired
  var startDate = dashboard.getRange("C2").getDisplayValue();
  var endDate = dashboard.getRange("D2").getDisplayValue();
  var sheet = spreadsheet.getSheetByName(sheetName);
  //chose the range within the specified dates, for this first locate the date column
  var startRow = 2;
  var dateColumn = sheet.getRange(startRow,1,sheet.getLastRow(), 1);
  var dates = dateColumn.getDisplayValues().flat();
  var firstRow = dates.indexOf(startDate)+startRow;
  var lastRow = dates.lastIndexOf(endDate)+startRow;
  //now get the range between (and including) those rows
  var range = sheet.getRange(firstRow, 1, lastRow-firstRow+1, sheet.getLastColumn());
  //Sorting and removing duplicates
  // You need to specify by which column you want to sort your data, in this sample it it column 3 - that it column C
  var column = 3;
  range.sort({column: column, ascending:true});
  range.removeDuplicates([column]);
  //now delete empty rows if any
  for (var i = range.getHeight(); i >= 1; i--){
    if(range.getCell(i, 1).isBlank()){
      sheet.deleteRow(range.getCell(i, 1).getRow());
    }
  }
  //Protecting data 
  var timeZone = Session.getScriptTimeZone();
  var stringDate = Utilities.formatDate(new Date(), timeZone, 'dd/MM/yy HH:mm');
  var me = Session.getEffectiveUser();
  var description = 'Protected on ' + stringDate + ' by ' + me;
  var protection = range.protect().setDescription(description)
  //protection.setDomainEdit(false);   
  protection.addEditor(me);
  protection.removeEditors(protection.getEditors());
  if (protection.canDomainEdit()) {
    protection.setDomainEdit(false);
  }
}

Important

The sample above will work if your sheet is sorted by dates (ascending) - as you specified in the comments. However, once the data is sorted by column C and not dates anymore it might not work as intended.

Sidenote:

From your code I understand that you recorded it as a macro rather than writing it from scratch (visible by the (unnecessary) calls of activate()).

I very much recommend you to take some time to study Apps Script in roder to understand your code in depth and be able to perform modificaitons and adjustments according to your needs.

There is the basic tutorial for Apps Script in general, samples and explanations for Google Sheets in specific and the references for all available Apps Script methods, whereby most methods feature a code sample.