Q: Google Sheets Script "InternalError: Cannot convert to (class)" - google-apps-script

I wrote a Google Script for my spreadsheet (a lot of copy/paste online) that is intended to transfer a row of data from one sheet to another sheet if a date matches to the current date (it runs once per day). The code works, with one exception of row 24 where I get an error of "Cannot conver XXX to (class)" with XXX representing the data in that row. It seems it may be something with how I'm defining the variable source_range but I just can't get it to work! Any help is much appreciated. The goal is that within the for loop it copies the "current" row of data based on variable i. If I change the source_range parameters to something static (like row 3, for example) it works perfectly.... but of course then it only pastes row 3 every time which is not what I desire. Any suggestions help!
function AutomationEnroll() {
try{
var spreadsheet = SpreadsheetApp.openById("XXXHIDDENXXX");
var source_sheet = SpreadsheetApp.setActiveSheet(spreadsheet.getSheets() [2]);
var target_sheet = SpreadsheetApp.setActiveSheet(spreadsheet.getSheets()[3]);
var startRow = 2; // First row of data to process
var numRows = source_sheet.getLastRow()-1; // Number of rows to process
var dataRange = source_sheet.getRange(startRow, 1, numRows, source_sheet.getLastColumn());
// Fetch values for each row in the Range.
var data = dataRange.getValues();
for (i in data) {
var row = data[i];
//Logger.log(sheetDate);
var TodayDate = Utilities.formatDate(new Date(),'GMT','EEE, d MMM yyyy')
var SurgeryDate = Utilities.formatDate(new Date(row[1]),'GMT', 'EEE, d MMM yyyy')
if (TodayDate == SurgeryDate){
//I now need it to copy row[i] in sheet[2] over to the last_row in sheet[3] ;
var source_range = source_sheet.getRange(data[i], 1, 1, 12) //error converting to (class)
var last_row = target_sheet.getLastRow();
target_sheet.insertRowAfter(last_row);
var target_range = target_sheet.getRange("A"+(last_row+1)+":L"+(last_row+1));
source_range.copyTo(target_range);
}
}
}catch(err){
Logger.log(err.lineNumber + ' - ' + err);
}
}`

Related

Add date and text at the last row after a copy paste

I'm breaking my head here trying to figure it out a way to achieve this in a simples way.
I got working a code that copies a range from a sheet and paste in another at the last row.
function copyInfo() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var copySheet = ss.getSheetByName("Review");
var pasteSheet = ss.getSheetByName("Log");
// get source range
var source = copySheet.getRange(3,2,15,5);
// get destination range
var destination = pasteSheet.getRange(pasteSheet.getLastRow()+1,3,15,5);
// copy values to destination range
source.copyTo(destination, SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
}
The problem is, at the destination sheet, I still have columns A and B empty. By each row in the source (varies from time to time) copied, I need to place at column A todays date and column B the text "Review".
Like: Date (column A) | Revview (column B) | Pasted Data from Source (column C and other)
Do I need to write a completely new code to check for empty or can I implement both solutions into this code?
The code should fill the two cells before your data. It also caters for the setup where you have three areas in the same sheet.
Each copyInfoXd_button function will be assigned to your button image.
function copyInfo1d_button() {
copyInfo(2, "B1:B");
}
function copyInfo7d_button() {
copyInfo(9, "I1:I");
}
function copyInfo30d_button() {
copyInfo(16, "P1:P");
}
function copyInfo(startingCol, subjectCol)
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var copySheet = ss.getSheetByName("Review");
var pasteSheet = ss.getSheetByName("Log");
const sourceCols = 4; //actual number of columns to copy, not including the empty column H
const destCols = 6; //actual number of columns to write to, from DATE to OBS
//find the last row from a colum
var avals = copySheet.getRange(subjectCol).getValues();
var lastRow = avals.filter(String).length;
var rowsToCopy = lastRow - 2; //skip the first 2 rows as they are headers
// get source range
var source = copySheet.getRange(3,startingCol,rowsToCopy,sourceCols); // row, column, number of rows and number of columns
// get destination range
var destination = pasteSheet.getRange(pasteSheet.getLastRow()+1,1,rowsToCopy,destCols);
var data = [];
var sourceValues = source.getValues();
//construct new data for the destination range
for (var row in sourceValues) {
var destRow = [Utilities.formatDate(new Date(), ss.getSpreadsheetTimeZone(), 'MMMM dd, yyyy'), 'Review'];
destRow = destRow.concat(sourceValues[row]);
data.push(destRow);
}
destination.setValues(data);
}
I made it! I'm so proud! Thank you all for the help!
I'm beginning to do stuff like this!
After the Charles answer I looked for a way to add to each row. However, I know the code might not be too elegant, and I couldn't figured it out a way of doing using script itself, so I added a helper cell inside the sheet in H1 and H2.
Here's the solution:
function copyInfo24() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var copySheet = ss.getSheetByName("Review");
var pasteSheet = ss.getSheetByName("Log");
// get source range
var source = copySheet.getRange(4,2,99,5);
// get destination range
var destination = pasteSheet.getRange(pasteSheet.getLastRow()+1,3,99,5);
// copy values to destination range
source.copyTo(destination, SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
// Sets date at Column A and "Review" at Column B.
var data = [];
for(var i =0; i < copySheet.getRange("H1").getValue(); i++) {
data.push([Utilities.formatDate(new Date(), ss.getSpreadsheetTimeZone(), 'MMMM dd, yyyy'), 'Review']);
}
reviewRange = pasteSheet.getRange(pasteSheet.getLastRow()-copySheet.getRange("H2").getValue(), 1, copySheet.getRange("H1").getValue(), 2)
reviewRange.setValues(data); //set the date and 'Review' data
}

Macro Google Sheet "Macro action change"

I would need a change in how this macro works
function getDynamicRow(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('B.Wioski');
var sourceRange = sheet.getRange('A1:F26500');
var data = sourceRange.getValues(); // Array of arrays [[Row1],[Row1],[Row3]]
// add data to next empty row in the static sheet.
var targetSheet = ss.getSheetByName('TW');
targetSheet.getRange(targetSheet.getLastRow() + 1, 1, data.length, data[0].length).setValues(data);
}
currently the macro places the entries one under the other and I would need it to place the entries to the right of the preview image below
additionally, I would need a macro to delete data older than 5-7 days
Suggestion
Perhaps you can try this implementation below:
Script:
function getDynamicRow(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('B.Wioski');
var sourceRange = sheet.getRange('A1:F'+sheet.getLastRow());
var data = sourceRange.getValues(); // Array of arrays [[Row1],[Row1],[Row3]]
data.push(["","","","","",new Date()]); //added a timestamp for reference on deleting older data
// add data to next empty row in the static sheet.
var targetSheet = ss.getSheetByName('TW');
var currentCol = targetSheet.getDataRange().getNumColumns();
if(currentCol != 1)currentCol = currentCol+1;
targetSheet.getRange(1, currentCol, data.length, data[0].length).setValues(data);
}
Sample Result:
After running the script once:
After running it again (so on & so forth) :
As for deleting data older than 5 - 7 days, here's my implementation that you can try:
Since I have added a timestamp on every copied data on sheet TW, you can refer to this sample script below:
UPDATED
Script:
function deleteOlderThan5To7Days(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var targetSheet = ss.getSheetByName('TW');
var row = targetSheet.getDataRange().getLastRow();
var col = targetSheet.getDataRange().getLastColumn();
var data = targetSheet.getRange(row,1,1,col).getValues();
for(x=0; x< data[0].length; x++){
var curCol = x + 1;
if(data[0][x] != ""){
var getDate = Utilities.formatDate(new Date(data[0][x]), SpreadsheetApp.getActive().getSpreadsheetTimeZone(), "dd");
var checkDays = getDate - new Date().getDate();
if(new Date(data[0][x]).getMonth()+1 == new Date().getMonth()+1){ //If data timestamp is within the current month
if(checkDays > 5){ //Check if the timestamp of the data is more than 5 - 7 days
Logger.log("Current row date: "+data[0][x]+"\n"+"Found on column #"+curCol+ " is "+checkDays+" days older");
Logger.log("Delete data column " + curCol);
targetSheet.insertColumnsAfter(targetSheet.getLastColumn(),1); //add a blank column on every deletion
targetSheet.deleteColumn(curCol); //delete the points column data
return;
}else{
//Data timestamp is NOT older than 5-7 days");
}
}else{ //Data was copied last month or older that the current month
Logger.log("Current row date: "+data[0][x]+"\n"+"Found on column #"+curCol+ " is old");
Logger.log("Delete data column "+ curCol);
targetSheet.insertColumnsAfter(targetSheet.getLastColumn(),1); //add a blank column on every deletion
targetSheet.deleteColumn(curCol); //delete the points column data
return;
}
}
}
}
Sample Demonstration:
Let's say the first data was copied on November 2, 2021 or 11/2/2021 as seen here on the sample sheet on TW:
After running the sample function deleteOlderThan5To7Days, here's the result:
The execution logs for review:
NOTE:
Same removal process applies if ever the month is the same as the current month but older than 5-7 days.

Getting Specific Rows from Another Google Sheet via App Script Based on Multiple Cell Values

I would like to fetch data from another google sheet, then retrieve all columns(been successful with this one). As for the rows, I would only like to retrieve all rows with these values, "Mrm1, Mrm2, Mrm3" from Measurement Column.
For example, I have these on another sheet,Original Data
And on another sheet, I want the result to be like this: Expected Retrieved Data
So far, I have this, but I'm not sure how to apply what I need on the rows. This script is retrieving everything.
function Country A() {
var sss = SpreadsheetApp.openById("19-idD8PWuNxnvzRVqBlKhju2RkKxOe8nUQaf9ZMAcgk"); // Source spreadsheet - replace with actual key
var ss = sss.getSheetByName('Compiled'); // Source sheet - change to actual name
var numRows = ss.getLastRow() // get last row with data
var numColumn = ss.getLastColumn(); // get last column with data
var srange = ss.getRange(2,1,numRows,numColumn); // Source range - change to actual range
var values = srange.getDisplayValues();
var lss = SpreadsheetApp.getActiveSpreadsheet(); // Local spreadsheet - the one this script is in
var targetnumRows = lss.getSheetByName('Country A').getMaxRows();
var targetnumcolumns = lss.getSheetByName('Country A').getMaxColumns();
var ls = lss.getSheetByName('Country A').getRange(2,1,numRows,numColumn); // Local sheet - change to actual name and Local range - change to actual range
var TargetRange = lss.getSheetByName('Country A').getRange(2,1,targetnumRows,targetnumcolumns); // Local sheet - change to actual name and Local range - change to actual range
TargetRange.clear() //Clear Sheet Content
ls.setValues(values); // Copy from source sheet to local sheet
var sheet = lss.getSheetByName('Country A');
var maxColumns = sheet.getMaxColumns();
var lastColumn = sheet.getLastColumn();
var maxRows = sheet.getMaxRows();
var lastRow = sheet.getLastRow();
if (maxColumns-lastColumn != 0){sheet.deleteColumns(lastColumn+1, maxColumns-lastColumn)};
if (maxRows-lastRow != 0){sheet.deleteRows(lastRow+1, maxRows-lastRow)};
var TargetHour = lss.getSheetByName('Country A').getRange(1,1).setValue(Utilities.formatDate(new Date(), "CST", "MM-dd-yyyy HH:mm:ss"));
}
Please help me. Thank you.
The script stores the values in a 2D array. You have to iterate through the elements and delete the elements where column E (Col number 4) (A=0, B=1,...E=4) is not equal to "Mrm1, Mrm2, Mrm3".
Then you paste the modified array in the second sheet.
Here is an example. You need to adapt it to your code.
function copyData() {
var ss = SpreadsheetApp.openById("YOUR-ID");
// Stores data as a 2D Array
var data = ss.getSheetByName("Sheet1").getRange("A1:M10").getValues();
// Remove rows where column E not equals "Mrm1, Mrm2, Mrm3"
for (var i = 0; i < data.length; i++) {
if (data[i][4] !== "Mrm1" && data[i][4] !== "Mrm2" && data[i][4] !== "Mrm3") {
data.splice(i,1);
i--;
}
}
// Paste data in sheet 2
var pasteRange = "A1:M" + (data.length);
ss.getSheetByName("Sheet2").getRange(pasteRange).setValues(data);
}

Add Timestamp if row is not empty

I have a script on a nightly trigger which does the following three things:
copies the contents of one spreadsheet (spreadsheet 1) to another (spreadsheet 2).
adds a timestamp column to column A each time the script is run.
deletes rows if there are certain values in column D.
The first time the script runs, everything works fine (i.e. when the new spreadsheet 2 is still blank). The subsequent times it runs it encounters issues.
With some research I have discovered that is because any blank rows in the source sheet (spreadsheet 1) are copied to the new spreadsheet (spreadsheet2) and a timestamp is placed in the blank rows. This leaves a bunch of rows that only have a timestamp. By the time the script gets to the part where it is supposed to delete rows based on values it gets thrown by these rows that only have a timestamp.
I am new to google script and was unable to find anything in the forum. Is there a way to modify the script so that the timestamp is only placed on rows where there is data?
var sourceSpreadsheetID = "spreadsheet1 ID";
var sourceWorksheetName = "BusinessDetails";
var targetSpreadsheetID = "spreadsheet2 ID";
var targetWorksheetName = "Sheet5";
function importBusinessDetailsData(){
var thisSpreadsheet = SpreadsheetApp.openById("spreadsheet1 ID");
var thisWorksheet = thisSpreadsheet.getSheetByName("BusinessDetails");
var thisData = thisWorksheet.getDataRange();
var toSpreadsheet = SpreadsheetApp.openById("spreadsheet2 ID");
var toWorksheet = toSpreadsheet.getSheetByName("Sheet5");
var toRange = toWorksheet.getRange(1,2, thisData.getNumRows(), thisData.getNumColumns())
toRange.setValues(thisData.getValues());
setTimeStamp()
main()
removeThenSetNewVals()
}
function setTimeStamp() {
SpreadsheetApp.getActive().getSheetByName('Sheet5')
.getRange('A2:A').setValue(new Date())
};
function main() {
var startTime = new Date().getTime();
var deleteSelectedRows = removeThenSetNewVals();
var runTime = (new Date().getTime() - startTime) / 1000;
Logger.log("Runtime is: " + runTime + " seconds");
};
function removeThenSetNewVals(){
var sheet = SpreadsheetApp.getActive().getSheetByName('Sheet5');
var range = sheet.getDataRange();
var pattern = new RegExp("value1|value2|value3");
var columnToSearch = 3;
var newRangeVals = range.getValues().filter(r => r[0] && !pattern.exec(r[columnToSearch]))
range.clearContent();
var numRows = newRangeVals.length;
var newRange = sheet.getRange(1,1, numRows, newRangeVals[0].length).setValues(newRangeVals);
var maxRows = sheet.getMaxRows();
}
As far as I understand, you only want to write entries to the new sheet which fit a particular condition. Basically, you want to filter the records before you write them, therefore consider the following:
var thisData = thisWorksheet.getDataRange();
var rowsWithData = thisData.filter( row => row[3] )
This states that only rows which contain truthy data will be considered, but feel free to fit the condition according to your needs.
References
filter()
truthy

move a row in google spreadsheet based on todays date

I'm trying to move a row of data from one sheet to another in the same spreadsheet based on a value of today's date.
In column "A", I have a date. I want to move the row if the date entered in column "A" is older than today's date. (it's a flight schedule for aircraft and I want to move flights that have already occured onto a sheet called "Past Flights".) The name of the active sheet is "Flight Schedule".
After the row is moved, I want it to delete off the "Flight Schedule" sheet. I know where to add scripts, but have no idea how to make this happen.
Here is what I have tried. I think on line "If (data.link >1..." data.link isn't the right one to use. But I can't find something for indicating older than todays date.
function approveRequests() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
sheet = ss.getActiveSheet(),
sheetName = sheet.getName(),
data = sheet.getDataRange().getValues();
if (sheetName == "Flight Shedule") {
var range = sheet.getActiveRange(),
startRow = range.getRowIndex(),
numRows = range.getNumRows(),
numCols = range.getNumColumns()
if (numCols == 9) {
if (data.length > 1) {
var values = range.getValues(),
nextSheet = ss.getSheetByName("Past Flight"),
lastRow = nextSheet.getLastRow();
nextSheet.getRange(lastRow+1,1,numRows,3).setValues(values);
sheet.deleteRows(startRow,numRows);
}
}
}
}
Any help would be huge!
Thanks!
Ok, I will go in with some general tips based on your current code first.
In your function you do a sheet = ss.getActiveSheet() which is redundant because you already have SpreadsheetApp.getActiveSpreadsheet().Also I would recommend to avoid this
var ss = SpreadsheetApp.getActiveSpreadsheet()
sheet = ss.getActiveSheet(),
sheetName = sheet.getName(),
data = sheet.getDataRange().getValues();
in favour of this:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var sheetName = sheet.getName();
var data = sheet.getDataRange().getValues();
which is much more easy to read and change without making mistakes.
data.length has nothing to do with current date, it will simply be the length of the array. So if you select 1 row of data, it will be 1, if you select 2 rows it will be 2 etc. .getValues() will return an array where data[row][col]. What you are looking for is getting the value of the flight time, converting it into a date object (not a google specific thing, just general javascript). Then use var now = new Date() and compare the two.
I would also recommend to re-think your if statements. There are a lot of better ways to grab the row data than selecting the row manually and then running the function. You can save a lot of lines of code should you decide to actually make this run automatically, because as it is, it will run only when called manually.
This sample is working:
function approveRequests() {
// Initialising
var ss = SpreadsheetApp.getActiveSpreadsheet();
var scheduleSheet = ss.getSheetByName("Flight Shedule");
var pastSheet = ss.getSheetByName("Past Flight");
var lastColumn = scheduleSheet.getLastColumn();
// Check all values from your "Flight Schedule" sheet
for(var i = scheduleSheet.getLastRow(); i > 0; i--){
// Check if the value is a valid date
var dateCell = scheduleSheet.getRange(i, 1).getValue();
if(isValidDate(dateCell)){
var today = new Date();
var test = new Date(dateCell);
// If the value is a valid date and is a past date, we remove it from the sheet to paste on the other sheet
if(test < today){
var rangeToMove = scheduleSheet.getRange(i, 1, 1, scheduleSheet.getLastColumn()).getValues();
pastSheet.getRange(pastSheet.getLastRow() + 1, 1, 1, scheduleSheet.getLastColumn()).setValues(rangeToMove);
scheduleSheet.deleteRow(i);
}
}
}
}
// Check is a valid date
function isValidDate(value) {
var dateWrapper = new Date(value);
return !isNaN(dateWrapper.getDate());
}
So yes, It's not the optimized solution (cause of the use of several sheet.getRange() method), but it's working and allowing to have a clear code.