Clear data every night from a spreadsheet - scripting

Teacher entered lunch count to a google spreadsheet...would like data triggered to zero every night...When teachers open next day...the count is zero
tried using the following code.gs
function myFunction() {
function ClearSheet() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("BuelLunch");
sheet.clearContents()
}
}

Related

Getting individual question scores(numbers) from Google forms to a google spreadsheet

I have a google form which is basically an assessment for students. Each question carries 1 point. When I connect my form to a specific spreadsheet, I get the total score of the student e.g 24/30
What I want to do:
Along with the total score, we want to get each question's score to go to the spreadsheet. Here is what we are trying to have:
I have no idea what to do. Please guide. Thanks
You can try with Apps Script. I tested the following script that can give you an idea on how to achieve this.
const TARGET_SPREADSHEET_ID = "spreadsheetID"; //Change according to your needs
const TARGET_SHEET_NAME = "sheetName"; //Change according to your needs
const SOURCE_FORM_ID = "formID"; //Change according to your needs
//Run this function only one time, this will create a trigger that will run function "onFormSubmitTrigger" whenever a student submits a response.
function installOnFormSubmitTrigger() {
const form = FormApp.openById(SOURCE_FORM_ID);
ScriptApp.newTrigger("onFormSubmitTrigger")
.forForm(form)
.onFormSubmit()
.create();
}
function onFormSubmitTrigger(e) {
const targetSpreadsheet = SpreadsheetApp.openById(TARGET_SPREADSHEET_ID);
const targetSheet = targetSpreadsheet.getSheetByName(TARGET_SHEET_NAME);
//GETTING RESPONSES
var itemResponses = e.response.getItemResponses();
var responses = itemResponses.map(itemResponse => itemResponse.getResponse()); //Get responses
var name = responses.splice(0,1); //extracting first element ("Full Name") of responses
//GETTING SCORES
var itemScores = e.response.getGradableItemResponses();
var scores = itemScores.map(itemScore => itemScore.getScore()); // Get response score
scores.splice(0,1); //removing score for the first element ("Full Name")
var newArr = name.concat(scores); //combining name and scores in one array
//newArr.splice (1,0,""); //you can add this line to insert blank between "Student Name" and "Q1"
targetSheet.appendRow(newArr); //Append scores to the sheet
}
Test the script by submitting a form and the target sheet should show students names and the score for each question they answered. If you have any questions let me know.

Binance API, simple GET price by ticker, not working

When I excute this code as Google Sheets Script, my first and subsequent attempts rarely retrieves the data from Binance. Occasionally it will work. Can anyone help?
function BINTickFetch(){
var rows=[],obj_array=null;
try {obj_array=JSON.parse(UrlFetchApp.fetch("https://api.binance.com/api/v3/ticker/price").getContentText());} catch (e) {obj_array=null;}
if (obj_array==null) {
Browser.msgBox("data not received from Binance. Try again");
return false;
}
else {
for (r in obj_array) rows.push([obj_array[r].symbol, parseFloat(obj_array[r].price)]);
var ss=SpreadsheetApp.getActiveSpreadsheet(),sheet=ss.getSheetByName('Binance24h');ss.getRange("Binance24h!A1").setValue(new Date());
try {var range=sheet.getRange(2,1,sheet.getLastRow(),2).clearContent();} catch(e) {Logger.log("error");}
if (rows==null) {Browser.msgBox("incomplete symbol data from Binance. Try again"); return false;}
range=sheet.getRange(2,1,rows.length,2); range.setValues(rows);
}
}
(got the code off the internet somewhere)
I am not familiar with that site and I use Yahoo finance. This will grab historical pricing for MSFT
function importCSVFromWeb() {
// Provide the full URL of the CSV file.
var csvUrl = "https://query1.finance.yahoo.com/v7/finance/download/MSFT?period1=1577806579&period2=1609428979&interval=1d&events=history&includeAdjustedClose=true";
var csvContent = UrlFetchApp.fetch(csvUrl).getContentText();
var csvData = Utilities.parseCsv(csvContent);
var sheet = SpreadsheetApp.getActiveSheet();
sheet.getRange(1, 1, csvData.length, csvData[0].length).setValues(csvData);
}
Yahoo uses unix date stamps for the beginning and ending periods
This is one way to convert dates to unix date stamps
function getUnixDateStamp(stdDate){
strDate = stdDate.yyyymmdd();
return Date.parse(strDate)/1000
}

Copy Excel OneDrive table to an specific cell on another Excel OneDrive table (Power Automate)

I need to copy data from one excel worksheet and paste (values only) on another worksheet using power automate + Office script
I started to creat a flow using the answer in the link bellow.
Power Automate: Copy Excel OneDrive table to the bottom of another Excel OneDrive table
The problem is I didnt understood the second script so I was not able to modify it to what I need ( that one paste on the end of the workbook)
SCRIPT on the link
For Run script I have
function main(workbook: ExcelScript.Workbook) {
const sheet = workbook.getWorksheets()[0];
let lastRow = sheet.getUsedRange(true).getLastCell().getRowIndex() + 1;
let rng = "A3:P" + lastRow
let tableTest = sheet.getRange(rng).getValues();
console.log(tableTest);
}
Then under Compose
#{outputs('Run_script')?['body']?['Logs'][0]}
Then Initialize the "RemoveString" variable
#{split(outputs('Compose'),' ')[0]}
Then Initialize the "NewString" variable
#{replace(outputs('Compose'),variables('RemoveString'),'')}
Then Run Script 2 and add "NewString" as the parameter.
function main(workbook: ExcelScript.Workbook, rangeTest: string) {
let table = workbook.getTable("BacklogTable");
let str = rangeTest;
let testerTest = JSON.parse(str);
table.addRows(null, testerTest);
}
The reason for RemoveString is to remove the Date & Time Stamp from the outputs
This requires a little different workflow.
Run Script
function main(workbook: ExcelScript.Workbook) {
const sheet = workbook.getWorksheets()[0];
let lastRow = sheet.getUsedRange(true).getLastCell().getRowIndex() + 1;
let rng = "A2:C" + lastRow
let tableTest = sheet.getRange(rng).getValues();
console.log(tableTest);
console.log(tableTest.length)
}
Compose
#{outputs('Run_script')?['body']?['Logs'][0]}
Compose 2
#{outputs('Run_script')?['body']?['Logs'][1]}
RemoveString
#{split(outputs('Compose'),' ')[0]}
NewString
#{replace(outputs('Compose'),variables('RemoveString'),'')}
RemoveString2
#{split(outputs('Compose_2'),' ')[0]}
NewString2
#{int(replace(outputs('Compose_2'),variables('RemoveString2'),''))}
Num
#{int(variables('NewString2'))}
Run Script 2
function main(workbook: ExcelScript.Workbook, rangeTest: string, length: number) {
let str = rangeTest;
const arr = JSON.parse(str);
let sheet = workbook.getWorksheet("Sheet2");
let rng = "A7:C" + (6 + length); //Change C to whichever column you want to end on
sheet.getRange(rng).setValues(arr);
sheet.getRange(rng).setNumberFormatLocal("0.00");
}
I may not be following this correctly, but you can also return values and pass them to another connector in Power Automate. Here is some documentation on how to return values with Office Scripts. Also, below is an example script with parameters and a number type return value. With returning values rather than console.logging them, you won't need to remove any output in your Flow steps. Let me know if you have any questions!
function main(
workbook: ExcelScript.Workbook,
issueId: string,
issueTitle: string): number {
// Get the "GitHub" worksheet.
let worksheet = workbook.getWorksheet("GitHub");
// Get the first table in this worksheet, which contains the table of GitHub issues.
let issueTable = worksheet.getTables()[0];
// Add the issue ID and issue title as a row.
issueTable.addRow(-1, [issueId, issueTitle]);
// Return the number of rows in the table, which represents how many issues are assigned to this user.
return issueTable.getRangeBetweenHeaderAndTotal().getRowCount();
}

Merging many spreadsheets into report file exceeds maximum execution time

I am using the following script to add rows of files from a student loop in the Google spreadsheet if credits are less than x. The script was working good but as the data in the spreadsheet is being added daily, now the script is throwing "Exceeded maximum execution time" error (we have more than 2000 files). As I am new to scripting I don't know how to optimize the code.
Could someone help me to optimize the code or any solution so that the execution time take less than 5 min. Every time you compare to an email, it has to be compared to many emails. Please Help!
function updated() {
//Final file data (Combined)
var filecombined = SpreadsheetApp.openById("XXXXXXXXXX");
var sheet2 = filecombined.getSheets();
//Folder with all the files
var parentFolder = DriveApp.getFolderById("YYYYYYYYYYYY");
var files = parentFolder.getFiles();
//Current Date
var fecha = new Date();
//Path for each file in the folder
while (files.hasNext()) {
var idarchivo = files.next().getId();
var sps = SpreadsheetApp.openById(idarchivo);
var sheet = sps.getSheetByName('STUDENT PROFILE');
var data = sheet.getDataRange().getValues();
var credits = data[5][1];
//Flat; bandera:1 (new row), bandera:2 (update row)
var bandera = 1;
//Take data from final file (Combined)
var data2 = sheet2[0].getDataRange().getValues();
//If credits are less than X: write
if (credits < 120) {
var email = data[2][1];
var lastrow = filecombined.getLastRow();
var u = 0;
//comparison loop by email, if found it, update and exit the loop
while (u < lastrow) {
u = u + 1;
if (email == data2[u - 1][1]) {
sheet2[0].getRange(u, 3).setValue(credits);
sheet2[0].getRange(u, 4).setValue(fecha);
u = lastrow;
bandera = 2;
}
}
//if that email does not exist, write a new row
if (bandera == 1) {
var nombre = data[0][1];
sheet2[0].getRange(lastrow + 1, 1).setValue(nombre);
sheet2[0].getRange(lastrow + 1, 2).setValue(email);
sheet2[0].getRange(lastrow + 1, 3).setValue(credits);
sheet2[0].getRange(lastrow + 1, 4).setValue(fecha);
}
}
}
SpreadsheetApp.flush();
}
The questioner's code is taking taking more than 4-6 minutes to run and is getting an error Exceeded maximum execution time.
The following answer is based solely on the code provided by the questioner. We don't have any information about the 'filecombined' spreadsheet, its size and triggers. We are also in the dark about the various student spreadsheets, their size, etc, except that we know that there are 2,000 of these files. We don't know how often this routine is run, nor how many students have credits <120.
getvalues and setvalues statements are very costly; typically 0.2 seconds each. The questioners code includes a variety of such statements - some are unavoidable but others are not.
In looking at optimising this code, I made two major changes.
1 - I moved line 27 var data2 = sheet2[0].getDataRange().getValues();
This line need only be executed once and I relocated it at the top of the code just after the various "filecombined" commands. As it stood, this line was being executed once for every student spreadsheet; this along may have contributed to several minutes of execution time.
2) I converted certain setvalue commands to an array, and then updated the "filecombined" spreadsheet from the array once only, at the end of the processing. Depending on the number of students with low credits and who are not already on the "filecombined" sheet, this may represent a substantial saving.
The code affected was lines 47 to 50.
line47: sheet2[0].getRange(lastrow+1, 1).setValue(nombre);
line48: sheet2[0].getRange(lastrow+1, 2).setValue(email);
line49: sheet2[0].getRange(lastrow+1, 3).setValue(credits);
line50: sheet2[0].getRange(lastrow+1, 4).setValue(fecha);
There are setvalue commands also executed at lines 38 and 39 (if the student is already on the "filecombined" spreadsheet), but I chose to leave these as-is. As noted above, we don't know how many such students there might be, and the cost of these setvalue commands may be minor or not. Until this is clear, and in the light of other time savings, I chose to leave them as-is.
function updated() {
//Final file data (Combined)
var filecombined = SpreadsheetApp.openById("XXXXXXXXXX");
var sheet2 = filecombined.getSheets();
//Take data from final file (Combined)
var data2 = sheet2[0].getDataRange().getValues();
// create some arrays
var Newdataarray = [];
var Masterarray = [];
//Folder with all the files
var parentFolder = DriveApp.getFolderById("YYYYYYYYYYYY");
var files = parentFolder.getFiles();
//Current Date
var fecha = new Date();
//Path for each file in the folder
while (files.hasNext()) {
var idarchivo = files.next().getId();
var sps = SpreadsheetApp.openById(idarchivo);
var sheet = sps.getSheetByName('STUDENT PROFILE');
var data = sheet.getDataRange().getValues();
var credits = data[5][1];
//Flat; bandera:1 (new row), bandera:2 (update row)
var bandera = 1;
//If credits are less than X: write
if (credits < 120){
var email = data[2][1];
var lastrow = filecombined.getLastRow();
var u = 0;
//comparison loop by email, if found it, update and exit the loop
while (u < lastrow) {
u = u + 1;
if (email == data2[u-1][1]){
sheet2[0].getRange(u, 3).setValue(credits);
sheet2[0].getRange(u, 4).setValue(fecha);
u = lastrow;
bandera = 2;
}
}
//if that email does not exist, write a new row
if(bandera == 1){
var nombre = data[0][1];
Newdataarray = [];
Newdataarray.push(nombre);
Newdataarray.push(email);
Newdataarray.push(credits);
Newdataarray.push(fecha);
Masterarray.push(Newdataarray);
}
}
}
// update the target sheet with the contents of the array
// these are all adding new rows
lastrow = filecombined.getLastRow();
sheet2[0].getRange(lastrow+1, 1, Masterarray.length, 4);
sheet2[0].setValues(Masterarray);
SpreadsheetApp.flush();
}
As I mentioned in my comment, the biggest issue you have is that you repeatedly search an array for a value, when you could use a much faster lookup function.
// Create an object that maps an email address to the (last) array
// index of that email in the `data2` array.
const knownEmails = data2.reduce(function (acc, row, index) {
var email = row[1]; // email is the 2nd element of the inner array (Column B on a spreadsheet)
acc[email] = index;
return acc;
}, {});
Then you can determine if an email existed in data2 by trying to obtain the value for it:
// Get this email's index in `data2`:
var index = knownEmails[email];
if (index === undefined) {
// This is a new email we didn't know about before
...
} else {
// This is an email we knew about already.
var u = ++index; // Convert the array index into a worksheet row (assumes `data2` is from a range that started at Row 1)
...
}
To understand how we are constructing knownEmails from data2, you may find the documentation on Array#reduce helpful.

Need a more efficient solution than looping

I am building a spreadsheet that tracks work in progress as it moves through steps of a manufacturing process.
Each step of the process has a column with the total parts moved to each stage. To the left of this column is a column for number of parts moved to the stage (parts move through a few at a time).
My scrpit then takes the values in the "add" column, adds them to the "total" column, then reset the "add" column to "".
Here's the code:
function addColumns() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
// ss is now the spreadsheet the script is associated with
var sheet = ss.getSheets()[0]; // sheets are counted starting from 0
// sheet is the first worksheet in the spreadsheet
for (var i=4; i<500; i++ ) {
if(sheet.getRange(i,1).getValue()>0){ //Only run if order number not empty
//Breakout Column
var add = sheet.getRange(i,6);
var total = sheet.getRange(i,7);
total.setValue(total.getValue() + add.getValue());
add.setValue("");
//CNC Column
var add = sheet.getRange(i,8);
var total = sheet.getRange(i,9);
total.setValue(total.getValue() + add.getValue());
add.setValue("");
//CutSand Column
var add = sheet.getRange(i,10);
var total = sheet.getRange(i,11);
total.setValue(total.getValue() + add.getValue());
add.setValue("");
//Lasered Column
var add = sheet.getRange(i,12);
var total = sheet.getRange(i,13);
total.setValue(total.getValue() + add.getValue());
add.setValue("");
//To Finishing Column
var add = sheet.getRange(i,14);
var total = sheet.getRange(i,15);
total.setValue(total.getValue() + add.getValue());
add.setValue("");
// Defective Column
var add = sheet.getRange(i,17);
var total = sheet.getRange(i,18);
total.setValue(total.getValue() + add.getValue());
add.setValue("");
//Etsy Column
var add = sheet.getRange(i,20);
var total = sheet.getRange(i,21);
total.setValue(total.getValue() + add.getValue());
add.setValue("");
}
if(sheet.getRange(i,4).getValue()<1){i=500} //Once you find a blank order exit the loop
}
}
My code as written does accomplish this; it does exactly what I need. The problem is that since the code is accessing the spreadsheet on each loop it takes almost a full second per cell to run, and with 7 steps per order it can take minutes at a time to run through with lots of orders...
This is a pretty simple mathematical task, so there has to be a more efficient way of doing it, I just haven't been able to find the right keywords to describe what I need to do.
I am quite happy to learn whatever needs to be done, just need to know what direction to head.
Thanks in advance!
I would suggest to do something like this: (not tested)
function addColumns() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheets()[0]; // Refers to the first worksheet in the spreadsheet
var data = sheet.getDataRange().getValues(); // Acquires all values of the sheet
for (var i = 3; i < data.length; i++) { // Loop over every row
if (data[i][0].length > 0) { // Check if first column has a value
// Breakout
sheet.getRange(i+1,7).setValue(parseFloat(data[i][6]) + parseFloat(data[i][5]));
sheet.getRange(i+1,6).clear();
// Repeat code above for other columns
}
}
}
This code acquires all the data from the sheet instead of looping over a fixed amount of 500 rows. Assuming that your data starts at row 4, I've implemented this in the code above as well.
Variable data acquires all the data at one moment instead of trying to fetch values of every range (cell) all the time. I expect that this will save your script quite some time.
Because we acquire the data at once, the script sees the value as a string. Before we calculate the new value of the total column, we parse the value as a float (a number with decimals).
The code above is not tested as I don't have a sheet ready in the same format as you do but I think the logic is clear and if it doesn't work I suppose you should be able to adjust it to work for your sheet.