Google Sheets Macro not completing - google-apps-script

I'm trying to parse a CSV file using Google Sheets macros. I've recorded all the steps as individual macros, and one by one they work fine, but when I combine into one macro, it doesn't run properly. The point at which it stops working is after the PODdateformatting part has completed and it's run through the first three lines of Daystodeliverformula. Cell H2 is populated with the formula, but the formula doesn't then autofill down the rest of the column. Any ideas? Or indeed, am I going about this all wrong and need a good talking to? :-)
function TheWholeShebang() {
var spreadsheet = SpreadsheetApp.getActive(); // start of DeletedUnwantedColumns
spreadsheet.getRange('AA:DE').activate();
spreadsheet.getActiveSheet().deleteColumns(spreadsheet.getActiveRange().getColumn(), spreadsheet.getActiveRange().getNumColumns());
spreadsheet.getRange('W:X').activate();
spreadsheet.getActiveSheet().deleteColumns(spreadsheet.getActiveRange().getColumn(), spreadsheet.getActiveRange().getNumColumns());
spreadsheet.getRange('R:U').activate();
spreadsheet.getActiveSheet().deleteColumns(spreadsheet.getActiveRange().getColumn(), spreadsheet.getActiveRange().getNumColumns());
spreadsheet.getRange('H:P').activate();
spreadsheet.setCurrentCell(spreadsheet.getRange('P1'));
spreadsheet.getActiveSheet().deleteColumns(spreadsheet.getActiveRange().getColumn(), spreadsheet.getActiveRange().getNumColumns());
spreadsheet.getRange('A:E').activate();
spreadsheet.setCurrentCell(spreadsheet.getRange('E1'));
spreadsheet.getActiveSheet().deleteColumns(spreadsheet.getActiveRange().getColumn(), spreadsheet.getActiveRange().getNumColumns()); // end of DeletedUnwantedColumns
var spreadsheet = SpreadsheetApp.getActive(); // start of Addcolumnsandheaderlabels
spreadsheet.getRange('A:F').activate();
spreadsheet.getActiveSheet().insertColumnsAfter(spreadsheet.getActiveRange().getLastColumn(), 6);
spreadsheet.getActiveRange().offset(0, spreadsheet.getActiveRange().getNumColumns(), spreadsheet.getActiveRange().getNumRows(), 6).activate();
spreadsheet.getRange('G1').activate();
spreadsheet.getCurrentCell().setValue('POD Date (formatted)');
spreadsheet.getRange('H1').activate();
spreadsheet.getCurrentCell().setValue('Days to Deliver');
spreadsheet.getRange('G2').activate(); // end of Addcolumnsandheaderlabels
var spreadsheet = SpreadsheetApp.getActive(); // start of PODdateformatting
spreadsheet.getRange('G2').activate()
.setFormula('=DATE(LEFT(D2,4),mid(D2,5,2),right(D2,2))');
spreadsheet.getActiveRange().autoFillToNeighbor(SpreadsheetApp.AutoFillSeries.DEFAULT_SERIES); // end of PODdateformatting
var spreadsheet = SpreadsheetApp.getActive(); //start of Daystodeliverformula
spreadsheet.getRange('H2').activate()
.setFormula('=NETWORKDAYS(E2,G2,Instructions!$B$15:$B$40)-1');
spreadsheet.getActiveRange().autoFillToNeighbor(SpreadsheetApp.AutoFillSeries.DEFAULT_SERIES); // end of Daystodeliverformula
};

After messing about with range.autoFillToNeighbor() and failing (see comments above) I was still suspicious about that method.
When I used this the range.autoFill() method instead it all worked fine. See code below.
// start of PODdateformatting
//var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('G2').activate().setFormula('=DATE(LEFT(D2,4),mid(D2,5,2),right(D2,2))');
var sourceRange = spreadsheet.getRange("G2:G2");
var destination = spreadsheet.getRange("G2:G369");
sourceRange.autoFill(destination,SpreadsheetApp.AutoFillSeries.DEFAULT_SERIES);
//start of Daystodeliverformula
var spreadsheet = SpreadsheetApp.getActive();
var range = spreadsheet.getRange('H2').activate().setFormula('=NETWORKDAYS(E2,G2,Instructions!$B$15:$B$40)-1');
var sourceRange = spreadsheet.getRange("H2:H2");
var destination = spreadsheet.getRange("H2:H369");
sourceRange.autoFill(destination,SpreadsheetApp.AutoFillSeries.DEFAULT_SERIES);
In hindsight I now believe that range.autoFillToNeighbor() was never right for your use case in the first place!
range.autoFillToNeighbor() expects to build autofill formulas based on data that is contained in neighbouring columns. It tries to do this intelligently, but neighbouring columns do not contain anything useful! Its amazing it ever worked... maybe sometimes defaulting to range.autoFill()!
range.autoFill() on the other hand, just duplicates the formula (data) above or below ( NOT looking to neighbouring cells for help).
You can copy and paste these over the corresponding functions in function TheWholeShebang() and should all work.
Note I assume a fixed range of 369 as per your data, but you can calculate this based on actual size if you prefer, in case no of rows is changed.

Related

Copy paste data to bottom last row on different worksheet with google sheet scrips

I'm trying to a script to work in which value from a specific range are pasted onto another sheet in a specific range. Problem is that not always the entire range of the input sheet is used and next time the function is used the data is pasted below the previously empty cells, see picture. Anybody any ideas?
function submitData2() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var database = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Data Logboek");
var source = ss.getSheetByName('Logboek Melding');
var dataToCopy = source.getRange('B2:J11');
var lastRow = database.getLastRow();
database.getRange('\'Logboek melding\'!B2:J11')
.copyTo(SpreadsheetApp.getActiveSpreadsheet()
.getSheetByName("Data Logboek").getRange(lastRow + 1,1)
,SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
}
Considerations
Your copying process seems confused:
You are selecting a range in your database Sheet using A1 notation, but you are referencing another Sheet.
You already have the Data Logboek Sheet saved in the database variable, there is no need to get it again from the SpreadsheetApp.
If the copying direction is source -> database you should call toCopy on a source range not the other way around.
Solution
Using your variable declaration you can translate your copying process to this:
var dataToCopy = source.getRange('A1Notation'); // Source data to copy on database
dataToCopy.copyTo(
database.getRange(lastRow + 1, 1), // Destination
SpreadsheetApp.CopyPasteType.PASTE_VALUES, // Copy Paste Type
false // Transpose
);
Reference
copyTo()

Google Sheets as Database

I'm trying to enter some data on a sheet in google sheets create a button that submits data onto another sheet. The other sheet will be like my database.
I have little to no JS experience. I'm able to create the button and link it my script but after that, I'm lost. This code worked well to get data to my database sheet. The problem is that the data stays on the same row and when I run the script the old data is erased.
function transfer() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var source = ss.getSheets()[0];
var destination = ss.getSheets()[1];
var range1 = source.getRange("B2");
range2.copyValuesToRange(destination,3,3,2,2);
range2.clearContent();
var range2 = source.getRange("C2");
range2.copyValuesToRange(destination,4,4,2,2);
range2.clearContent();
var range3 = source.getRange("D2");
range3.copyValuesToRange(destination,5,5,2,2);
range3.clearContent();
var range4 = source.getRange("C2");
range4.copyValuesToRange(destination,4,4,2,2);
range4.clearContent();
}
The other issue is I don't want the cells to be empty so I tried to set an alert.
var range1 = source.getRange("A2");
range1.copyValuesToRange(destination,2,2,2,2);
if (range1 ==!"");
Browser.msgBox("Please Enter A Date");
It prompted the msg box but still copied the data over.
Last I would like range1 to be like a unique ID. So if I put a value in A2 on my source sheet then it will auto-fill the other cells.
Here's the link if that helps.
https://docs.google.com/spreadsheets/d/1Zi7Oc0f5AlxcRRoFMM1Q1VkkM1Co6Yo8yYBY1TvkQMc/edit?usp=sharing
I recommend that you spend more time with the documentation. Personally, I've never considered putting buttons directly on the spreadsheet. I'd rather use the menu or a dialog or a sidebar. Also you generally pick up a lot of performance to replace the use of getValue() with getValues() where appropriate.
But here's another way to approach the problem your working on. Perhaps it will give something to think about. Have fun. Your buttons do look nice though. Very nice work.
function onOpen(){//This will put a post button on a menu
SpreadsheetApp.getUi().createMenu('My Menu')
.addItem('Post Data', 'postData')
.addToUi();
}
function postData() {//this will append data fromm 'DataEntry' to 'DB' with a timestamp spliced into it.
var ss=SpreadsheetApp.getActive();
var sh1=ss.getSheetByName('DataEntry');
var sh2=ss.getSheetByName('DB');
var rg1=sh1.getRange(sh1.getLastRow(),1,1,sh1.getLastColumn());
var vA=rg1.getValues();
vA[0].splice(0,0,Utilities.formatDate(new Date(),Session.getScriptTimeZone(),"E MMM dd, yyyy HH:mm:ss"));
sh2.appendRow(vA[0]);
}
This is what my DateEntry tab looks like:
This is what my DB tab looks like:
The post just adds the last row in DataEntry to the row after the last row in DB.

Copy Google sheet to a new sheet using cell value from 3rd sheet

I've been looking through the forums trying to search for the right way to do this but I can't seem to get it right. This is my first time using Google Apps Script so I'm learning as I'm going.
This is what I'm trying to do.
1) I want to duplicate all the contents of an old sheet into a new sheet. The script will determine the existing sheet name by referring to the cell b17 from 'Statistics' sheet, and will duplicate the old sheet, including values formats, references and formulas. The duplicated sheet will be named from cell c17 of the 'Statistics' sheet.
2) If possible I would also like to make the old sheet remove all the references and just keep the values and formats after the new sheet has been created. Similar to the copy all and ctrl-shift-v, except automating it in one fell swoop.
This is the code i currently have, not sure if it is correct
function newSheetLast() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var first = ss.getSheetByName("Statistics").activate();
var oldSheetName = ss.getRange('b17').getDisplayValue();
var newSheetName = ss.getRange('c17').getDisplayValue();
var sheet = ss.getSheetByName(oldSheetName).activate();
var source = SpreadsheetApp.getActiveSpreadsheet().duplicateActiveSheet(newSheetName);
};
I am getting stuck when trying to do 1. I tried the copyTo and can get the sheet to copy, but loose all the formatting and formulas when i copy over to the new sheet.
I am not sure how to even attempt 2
You variable naming and the way duplicating sheets works is a bit confusing.
You should really be able to duplicate a Sheet Object directly or at least be able to try and supply the name of the sheet you are trying to duplicate but no, it is on a Spreadsheet level and only works on the active sheet...
Duplicating a sheet carries formulae and formatting.
Overwriting with static values can be done with setValues(getValues).
function newSheetLast() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var settings = ss.getSheetByName("Sheet1");
var oldSheetName = settings.getRange('B17').getDisplayValue();
var oldSheet = ss.getSheetByName(oldSheetName);
var newSheetName = settings.getRange('C17').getDisplayValue();
ss
.getSheetByName(oldSheetName)
.activate()
.getParent()
.duplicateActiveSheet()
.setName(newSheetName);
oldSheet.getDataRange().setValues(oldSheet.getDataRange().getValues());
};

Copying Data Sheet1 to Sheet2 so you can sort & edit both sheets (google apps script?)

I am working in goggle sheets and think I need to use a google apps script to do what I want, but I am a psychologist at a non-profit University hospital trying to do some good and not a programmer (which probably shows) and I am desperately in need of help. I am trying to set up a series of spreadsheets to track participation in workshops for our treatment method.
1) I have a sheet “Participant_Registration” where basic information is entered
2) I want to transfer information from only the first four columns (A:D) of “Participant_Registration” to a second sheet “Learning_Sessions_Attendance”
3) I am also transferring the same information to a third sheet 'Consultation1_Attendance' – but I need to first filter and select only those people assigned to that group.
Here is a link to a copy of my spreadsheet.
https://docs.google.com/spreadsheets/d/17d0bT4LZOx5cyjSUHPRFgEZTz4y1yEL_tO3gtSJ4UJ8/edit?usp=sharing
More generically this is what I am trying to do. Is this possible in google app scripts? It seems it should be.
1) I have original data in sheet1
2) I want the first four columns (A:D) to transfer to sheet2 (it is fine if I need a trigger variable)
3) I want them to transfer in such a way that if you sort either sheet, the data are still fine (still linked to the right line).
4) Ideally if there is a change to the data in the source sheet (Sheet1) the same change will be made in Sheet2.
5) Ideally this would all happen automatically without human intervention through a script.
Any ideas?? I so need your help. I have been all over the forum, git hub, and done a ton of searches and tried following a lot of examples I saw but nothing works. I really need help.
Here are my sample scripts each with a problem:
//The following code copies a range from sheet1 to sheet2 as I wanted. A problem occurs if after if we copy the data from sheet1 we add data to other columns on sheet2. Later if we sort on some variable (which people are bound to do) if the function is deployed again it will overwrite data meaning the data from sheet1 are not connected to the right individual on sheet2
function CopyRange() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Participant_Registration");
var range = sheet.getRange("A14:D");
var values = range.getValues();
var target = ss.getSheetByName("Learning_Sessions_Attendance");
var target_range = target.getRange("A10:D");
range.copyTo(target_range);
}
So I tried again. This time I tried to just copy the last edited row from sheet1 to sheet2. This function does not appear to work for me.
function CopyRow2() {
// Get Spreadsheets
var source = SpreadsheetApp.openById("1egn6pnRd6mKMGuQxX_jtgwYDtkuMUv2QJItLdh7aIEs");
var target = SpreadsheetApp.openById("1egn6pnRd6mKMGuQxX_jtgwYDtkuMUv2QJItLdh7aIEs");
// Set Sheets
var source_sheet = source.getSheetByName("Participant_Registration");
var target_sheet = target.getSheetByName("Learning_Sessions_Attendance");
var rowIdx = source_sheet.getActiveRange().getRowIndex();
var rowValues = source_sheet.getRange(rowIdx,1,1,source_sheet.getLastRow()).getValues();
Logger.log(rowValues);
var destValues = [];
destValues.push(rowValues[0][0]);// copy data from col A to col A
destValues.push(rowValues[0][1]);//copy data from col B to col B
destValues.push(rowValues[0][2]);//copy data from col C to col C
destValues.push(rowValues[0][3]);//copy data from col D to col D
var dest=source.getSheets()[4];
dest.getRange(dest.getLastRow()+1,1,1,destValues.length).setValues([destValues]);//update destination sheet with selected values in the right order, the brackets are there to build the 2D array needed to write to a range
}
So I tried again and again. I have lots of examples but none seem to work.
Thanks so much.
Chandra
For that to happen automatically (one sheet's change updating another sheet), you will surely need an "event/trigger" to run a script whenever you change a cell. (that is the "onEdit()" function).
But since scripts are likely to fail sometimes (even when they are perfect, that's because of some Google issues), it's not guaranteed that the sheets will always contain the same data.
But, if I could suggest another way, do not let ID be optional. If that is a real ID (like the person ID card number), create another ID exclusively for working with the sheet.
I have edited your second sheet showing a suggestion of how to do it without using scripts. The only things you must be aware of are:
Do not create two people with the same ID.
You have to insert (only) the ID manually in the second sheet.
The VLOOKUP forumla will search for that ID in the first sheet and return the data in the same line. You can sort any sheet in whatever way you like. As long as you don't change people's IDs.
So, in sheet 2, use this in the First Name, Last Name and Email address:
=vlookup(A10,Participant_Registration!$A:$D,2,false)
=vlookup(A10,Participant_Registration!$A:$D,3,false)
=vlookup(A10,Participant_Registration!$A:$D,4,false)
Just extend this formula downwards
I hope this helps. I would avoid scripting for that at any cost. It would be my last resort. (Scripts also need to be changed if you want to rearrange your sheet, and if not, they might cause trouble, write over existing data...)
I also added a button (insert - drawing) and put a script in it (right button, click down arrow, "transfer? script" -- translated from Portuguese).
If you lock all four columns in sheet2 and lock the ID column in sheet 1, people will not be able to chang IDs and cause mess. They can edit people in sheet 1 and not change the formula in sheet2. Script is not affected by sorting or empty spaces (it adds the person in the first empty row it finds).
I added "named ranges" for the four column headers. (With named ranges, the script can refer to names instead of coordinates, which enables you to rearrange the sheet inserting and deleting columns, or moving them with CUT and paste - but the VLOOKUP formula will need manual update if you rearrange columns).
Here is the code: (it could get better if you manage to create dialog boxes and ask for the person's data inside that dialog, then you could lock everything - and you would need an edit button besides the add).
function AddPerson()
{
var S1Name = "Participant_Registration";
var S2Name = "Learning_Sessions_Attendance";
var ID1Name = "regID";
var ID2Name = "learnID";
//these vars are not used in this script
var FN1Name = "regFirstName";
var FN2Name = "learnFirstName";
var LN1Name = "regLastName";
var LN2Name = "learnLastName";
var Email1Name = "regEmail";
var Email2Name = "learnEmail";
var sSheet = SpreadsheetApp.getActiveSpreadsheet();
var Sheet1 = sSheet.getSheetByName(S1Name);
var Sheet2 = sSheet.getSheetByName(S2Name);
var ID1 = getRangeByName(sSheet, Sheet1.getName(), ID1Name);
var ID2 = getRangeByName(sSheet, Sheet2.getName(), ID2Name); Logger.log("ID2: " + ID2.getValue());
var Empty1 = getFirstEmpty(ID1);
var Empty2 = getFirstEmpty(ID2);
var Biggest1 = getBiggestID(ID1); Logger.log("Biggest 1: " + Biggest1);
var Biggest2 = getBiggestID(ID2); Logger.log("Biggest 2: " + Biggest2);
if (Biggest1 !== Biggest2)
Browser.msgBox("Warning: there are IDs in one sheet that are not in the other sheet");
var Biggest;
if (Biggest1 > Biggest2) Biggest = Biggest1;
else Biggest = Biggest2;
Biggest++;
Empty1.setValue(Biggest);
Empty2.setValue(Biggest);
}
function getFirstEmpty(Header)
{
while (Header.getValue() !== "")
{
Header = Header.offset(1,0);
}
return Header;
}
function getBiggestID(Header)
{
var Sheet = Header.getSheet();
var LastRow = Sheet.getLastRow();
var Values = Sheet.getRange(Header.getRow(), Header.getColumn(), LastRow - Header.getRow() + 1).getValues();
var len = Values.length;
var MaxID = 1;
for (var i = 0; i < len; i++)
{
var val = Number(Values[i]);
if (!isNaN(val) && val > MaxID)
MaxID = val;
}
return MaxID;
}
function getRangeByName(spreadSheet, sheetName, rangeName)
{
Logger.log("Trying range: " + "'" + sheetName + "'!" + rangeName);
return spreadSheet.getRangeByName("'" + sheetName + "'!" + rangeName);
}

Google Script: Conditionally copy rows from one sheet to another in the same spreadsheet

I read Google Script: Conditionally copy rows from one spreadsheet to another and could not make it work for me.
I need a script that will allow me to do the quoted text below. Please feel free to edit my spreadsheet to make a complete working script. I will keep the spreadsheet publicly available for anyone to copy the script for their own use. I really know nothing about script writing so I need it all spelled out. Sorry, I'm such a noob with all this, but this will help me learn.
https://docs.google.com/spreadsheet/ccc?key=0AoJdwy8V1ldHdFA5M1M1Wlp1NHZhcmxJOUZKVEU4X3c&usp=sharing
If Column B on sheet "All_Mileage" says "John" then I want that row to
be copy and pasted into Sheet "John" starting on row 3 and following.
If Column B on sheet "All_Mileage" says "Adam" then I want that row to
be copy and pasted into Sheet "Adam" starting on row 3 and following.
If Column B on sheet "All_Mileage" says "Mike" then I want that row to
be copy and pasted into Sheet "Mike" starting on row 3 and following.
I saw other scripts on here, but I couldn't get them to work. Like I said I'm greener than a sapling when it come to code.
Thanks a ton!
C.Lang is right, this is not a place to get readymade scripts ... but since this question is so common and has been aswered so often it took me a few minutes to write and test... so there it is :
var ss=SpreadsheetApp.getActiveSpreadsheet();// some global variables
var master = ss.getSheetByName('All_Mileage');
var colWidth = master.getMaxColumns();
function copyRowsOnCondition() {
var data = master.getDataRange().getValues();
for(n=2;n<data.length;++n){
if(data[n][1].length<16){ // if not pre-filled with your text
Logger.log(data[n][1])
var dest = ss.getSheetByName(data[n][1].toString().replace(/ /g,''));//remove any spaces that could be included in the name so the name = sheetName for sure
var destRange = dest.getRange(dest.getLastRow()+1,1);// the place to write
master.getRange(n+1,1,1,colWidth).copyTo(destRange);the copy itself value & format
}
}// loop
}
EDIT : since I used the name value in MasterSheet to find destination sheet I thought it might be usefull to handle the case where the destination sheet doen't exist by creating it using the same rule, ie. name = sheetName...
The other issue was that there was no way to know which rows had been already copied... so I made a version that handles all that, copying only the rows that are manually selected (even in only a single column) and change the background color to tell that these rows have been processed. I also added a menu for a minimal comfort ;-)
(how to keep busy on a cold sunday afternoon ;-)
var ss=SpreadsheetApp.getActiveSpreadsheet();
var master = ss.getSheetByName('All_Mileage');
var colWidth = master.getLastColumn();// last used col in masterSheet
var sheets = ss.getSheets();// number of sheets
function onOpen() {
var menuEntries = [ {name: "Copy selected Rows to sheets", functionName: "copyRowsOnConditionV2"},
];
ss.addMenu("Copy functions",menuEntries);// custom menu
}
function copyRowsOnConditionV2() {
var sheetNames = [];// array of existing sheet names
var sheets = ss.getSheets();// number of sheets
for(s=0;s<sheets.length;++s){sheetNames.push(sheets[s].getName())};
ss.getActiveSelection().setBackground('#ffffbb');
var selectedfirstRow = ss.getActiveSelection().getRowIndex();
var selectedHeigth = ss.getActiveSelection().getHeight()
var selectedFullRange = master.getRange(selectedfirstRow,1,selectedHeigth,colWidth);
var data = selectedFullRange.getValues();
for(n=0;n<data.length;++n){
if(data[n][1].length<16){
if(sheetNames.toString().match(data[n][1].toString().replace(/ /g,''))!=data[n][1].toString().replace(/ /g,'')){// if no sheet exist with this name
var newSheet = ss.insertSheet(data[n][1].toString().replace(/ /g,''),ss.getSheets().length);// then create it
master.getRange(1,1,2,colWidth).copyTo(newSheet.getRange(1,1));// and copy the headers on 2 first rows, then continue as usual
newSheet.getRange(1,1).setValue('Gas Mileage Log - '+data[n][1].toString().replace(/ /g,''));// set name in header
SpreadsheetApp.flush();
var sheets = ss.getSheets();// number of sheets
var sheetNames = [];// update array of existing sheet names
for(s=1;s<sheets.length;++s){sheetNames.push(sheets[s].getName())};
Logger.log(sheetNames)
};
var dest = ss.getSheetByName(data[n][1].toString().replace(/ /g,''));//find the destination sheet
Logger.log(data[n][1].toString().replace(/ /g,''))
var destRange = dest.getRange(dest.getLastRow()+1,1);// define range
master.getRange(selectedfirstRow+n,1,1,colWidth).copyTo(destRange);// and make copy below last row
}
}
}
Illustration below :