I am trying to create a folder structure and javascript files based on the sheet name and the Type (column a). Where Type "Spec" starts i'd like to group everything between where "Spec" begins and ends and input the content into that file using Google App Script.
Content Example
Desired Output
Code Example
The current code I have to get this is below. But I'm currently not able to add the middle content in.
function export() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
var filename = ""
for (var i in sheets) {
var name = sheets[i].getSheetName();
var fo = DriveApp.getFoldersByName(name);
var folder = fo.hasNext() ? fo.next() : DriveApp.getRootFolder().createFolder(name);
var values = sheets[i].getDataRange().getValues();
values.shift();
var type, title, value, emptyCode, code;
for (var j in values) {
[type, title, value, emptyCode, code] = values[j];
if (type == "Spec") {
var filename = title+'.js'
folder.getFilesByName(filename).hasNext() || folder.createFile(filename, code, MimeType.PLAIN_TEXT)
}
}
}
}
I had to edit the code slightly to add the title into the file and change the filename.
function generateStories() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
for (var i in sheets) {
var sheetName = sheets[2].getSheetName();
var fo = DriveApp.getFoldersByName(sheetName);
var folder = fo.hasNext() ? fo.next() : DriveApp.getRootFolder().createFolder(sheetName);
var values = sheets[2].getDataRange().getValues();
var type, title, code;
loop1:
for (var j = 0; j < values.length; j++) {
type = values[j][0];
if (type == "Spec") {
var title = values[j][1];
var filename = title+'.spec';
var code = values[j][4] + "\n";
loop2:
for (i = j+1; i < values.length; i++){
if (values[i][0] != "Spec"){
code += values[i][4] + "\n";
} else {
break loop2;
}
}
folder.getFilesByName(filename).hasNext() || folder.createFile(filename, code, MimeType.PLAIN_TEXT);
}
}
}
}
Two things
Values is a 2-D array - you need to address single values by specifying a row and a column indices, e.g. values[0][1] or values[i][4]
You need to specify the beginning and end of the content of each file. This can be done with the method indexOf() to verify in which rows type == "Spec" is fullfilled or with a combination of two nested for loops and an if statement.
Here is a sample of how to implement the functionality with the nested loops and statement combination:
function export() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
for (var i in sheets) {
var name = sheets[i].getSheetName();
var fo = DriveApp.getFoldersByName(name);
var folder = fo.hasNext() ? fo.next() : DriveApp.getRootFolder().createFolder(name);
var values = sheets[i].getDataRange().getValues();
var type, title, value, emptyCode, code;
loop1:
for (var j = 0; j < values.length; j++) {
type = values[j][0];
if (type == "Spec") {
var title = values[j][4];
var filename = title+'.js';
var code = "";
loop2:
for (i = j+1; i < values.length; i++){
if (values[i][0] != "Spec"){
code += values[i][4] + "\n";
} else {
break loop2;
}
}
folder.getFilesByName(filename).hasNext() || folder.createFile(filename, code, MimeType.PLAIN_TEXT);
}
}
}
}
Related
I am new to App Script. My code is below. I have been trying to take data out of an email and put it into different columns in google sheets. I have managed to achieve this and it works but because labels are applied to threads I get duplicates!
I have tried to figure out how to stop this from happening by using the email ID, date etc but I haven't been successful. Any help would be greatly appreciated.
function email_sheet() {
var ss = SpreadsheetApp.openById("");
var sheet = ss.getSheetByName("Sheet1");
var label = GmailApp.getUserLabelByName("ChosenLabel");
var threads = label.getThreads();
for (var i = 0; i < threads.length; i++) {
var messages = threads[i].getMessages();
for (var j = 0; j < messages.length; j++) {
var date = messages[j].getDate();
var body = messages[j].getPlainBody();
var name = "";
var accnum = "";
var paytype ="";
var amount = "";
var status = "";
/** Break Down the Email */
if(body.indexOf("Recipient : ")>0) {
var end = body.substring(body.indexOf("Recipient : ")+12,body.length);
name = end.substring(0, end.indexOf("\n"));
}
if(body.indexOf("AN")>0) {
var end = body.substring(body.indexOf("AN")+2,body.length);
account = end.substring(0, end.indexOf("\n"));
var [accnum, paytype] = account.split(" ");
}
if(body.indexOf("Amount : ")>0) {
var end = body.substring(body.indexOf("Amount : ")+9,body.length);
amount = end.substring(0, end.indexOf("\n"));
}
if(body.indexOf("Transaction Status : ")>0) {
var end = body.substring(body.indexOf("Transaction Status : ")+21,body.length);
status = end.substring(0, end.indexOf("\n"));
}
sheet.appendRow([date, name, accnum, paytype, amount, status]);
}
threads[i].removeLabel(label);
threads[i].addLabel(GmailApp.getUserLabelByName("All Transactions"))
}
} ```
You can for instance limit to unread mails and at the end mark them as already read, for instance
function mail() {
var requete ="is:unread {label:ChosenLabel label:OtherLabel}"
var ss = SpreadsheetApp.getActive().getSheetByName("Mail");
var threads = GmailApp.search(requete);
for (var i = 0; i < threads.length; i++) {
var messages = threads[i].getMessages();
for (var j = 0; j < messages.length; j++) {
var msg = messages[j].getPlainBody();
var sub = messages[j].getSubject();
var dat = messages[j].getDate();
ss.appendRow([dat, sub, msg])
}
}
GmailApp.markThreadsRead(threads);
}
I have two functions that I am trying to combine into one or find a better way to write one function to get the proper end result.
My goal is to read a range of cells and append them to the first open set of columns to the right of the current data. Depending on what is in the cell, I would like it to either pull the value or the function of the cell. Some cells are text, some are numbers imported from other sheets and some are formulas that react to those numbers. While doing that, I also need it to pull the conditional formatting from the cells as well.
Here is what I currently have written and need to somehow combine recordValue and recordFormulas into one function that properly pulls the right information to the appended cells.
Any and all help would be appreciated!
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('Record value')
.addItem('Record Formulas','testFormulas')
.addToUi();
}
function testFormulas() {
try {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sheet1");
var row1 = 1;
var column1 = 31;
var row2 = 40;
var column2 = 6;
var values = sheet.getRange(row1,column1,row2,column2).getValues();
var formulas = sheet.getRange(row1,column1,row2,column2).getFormulasR1C1();
var i=0;
var j=0;
var rules = sheet.getConditionalFormatRules();
var newRules = [];
var sheet = ss.getSheets()[0];
newRules = newRules.concat(rules);
for( i=1; i<formulas.length; i++ ) {
for( j=4; j<formulas[0].length; j++ ) {
if( formulas[i][j] !== "" ) values[i][j] = formulas[i][j];
}
}
Logger.log(values);
sheet.getRange(1,1,values.length,values[0].length).setValues(values);
sheet.getRange(1,1,values.length,values[0].length).setNumberFormats(format);
}
catch(err) {
Logger.log(err);}
var rowToWriteCounter = 1;
const firstColumnAvailable = sheet.getLastColumn() + 1;
var columnToWriteCounter;
for (var row in values) {
columnToWriteCounter = firstColumnAvailable;
for (var col in values[row]) {
//write to the new cell
sheet.getRange(rowToWriteCounter, columnToWriteCounter).setValue(values[row][col]).setBorder(true, true, true, true, false, false);
//this part checks for conditional formatting
for (var r = 0; r < rules.length; r++) {
var rule = rules[r];
//Get condition for each rule
var booleanCondition = rule.getBooleanCondition();
//Get the ranges to which each rule applies and iterate through
var ranges = rule.getRanges();
for (var i = 4; i < ranges.length; i++) {
var ruleColumn = ranges[i].getColumn();
var ruleRow = ranges[i].getRow();
//If condition isn't null and edited column is the same as the one in the range, add rule
if ((ruleColumn == Number(col)+1) && (ruleRow == Number(row)+1) && (booleanCondition != null)) {
var newRule = SpreadsheetApp.newConditionalFormatRule()
.withCriteria(booleanCondition.getCriteriaType(), booleanCondition.getCriteriaValues())
.setBackground(booleanCondition.getBackgroundObject())
.setBold(booleanCondition.getBold())
.setItalic(booleanCondition.getItalic())
.setRanges([sheet.getRange(rowToWriteCounter, columnToWriteCounter)])
.build();
newRules.push(newRule);
}
}
}
sheet.setConditionalFormatRules(newRules);
columnToWriteCounter++;
}
rowToWriteCounter++;
}
}
Here is a simple test case for combining values and formulas.
function test() {
try {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName("Sheet3");
var values = sh.getDataRange().getValues();
var formulas = sh.getDataRange().getFormulas();
var format = sh.getDataRange().getNumberFormats();
var i=0;
var j=0;
for( i=0; i<formulas.length; i++ ) {
for( j=0; j<formulas[0].length; j++ ) {
if( formulas[i][j] !== "" ) values[i][j] = formulas[i][j];
}
}
Logger.log(values);
sh = ss.getSheetByName("Sheet4");
sh.getRange(1,1,values.length,values[0].length).setValues(values);
sh.getRange(1,1,values.length,values[0].length).setNumberFormats(format);
}
catch(err) {
Logger.log(err);
}
}
I'm currently developing a sheet that shows results from a set of data based on some filters but the data loads to slowly when getting the results, I've tried to follow the Best Practices from Google Documentacion with no luck, how can I set an array for the data to load faster?
Below is the code commented with what I've already tried
function realizarBusqueda() {
var inicio = SpreadsheetApp.getActive().getSheetByName("INICIO");
var aux_tags = SpreadsheetApp.getActive().getSheetByName("Aux_Tags");
var data = SpreadsheetApp.getActive().getSheetByName("Data");
var data_lc = data.getLastColumn();
var data_lr = data.getLastRow();
var searchRange = data.getRange(2,1, data_lr, data_lc);
var inicio_lc = inicio.getLastColumn();
inicio.getRange("A8:L1000").clearContent();
inicio.getRange("A8:L1000").clearFormat();
var countRows = inicio.getMaxRows();
inicio.deleteRows(20, (20-countRows)*-1);
if (inicio.getRange("B4").isBlank()) {
inicio.getRange("A8:L1000").clearContent();
inicio.getRange("A8:L1000").clearFormat();
var countRows = inicio.getMaxRows();
inicio.deleteRows(20, (20-countRows)*-1);
SpreadsheetApp.flush();
}
else if ((inicio.getRange("B4").getValue() != "" &&
inicio.getRange("C4").getValue() === "")) {
//filtrado 1
var arrayDatos = searchRange.getValues();
var inicio_fr = 8;
//var row = new Array(11);
for (var j = 2; j <= data_lr; j++) {
//row[j] = new Array(data_lr);
if (aux_tags.getRange("P2").getValue() === arrayDatos[j-2][4]) {
var inicio_fc = 1;
for (var i = 0; i < arrayDatos[j-2].length; i++) {
//row[j][i] = arrayDatos[j-2][i];
var row = arrayDatos[j-2][i];
inicio.getRange(inicio_fr, inicio_fc).setValue(row);
inicio_fc++;
}
inicio_fr++;
}
//inicio.getRange("A8").setValues(row);
}
}
I expect the output to load lots faster, currently what I've tried is commented, the code as-is is working but too slow
I just wanted to update this subject because I figured out myself, see attached the new code with the use of new 2D arrays
...
//filtrado 1
var arrayDatos = searchRange.getValues();
var inicio_fr = 8;
var rows = [];
var row = [];
for (var j = 2; j <= data_lr; j++) {
if (aux_tags.getRange("P2").getValue() === arrayDatos[j-2][4]) {
var inicio_fc = 1;
for (var i = 0; i < arrayDatos[j-2].length; i++) {
row.push(arrayDatos[j-2][i]);
if (i == 11) {
rows.push(row);
row = [];
}
}
}
}
inicio.getRange(8, 1, rows.length, rows[0].length).setValues(rows);
}
Now instead of writing on row at a time, I just write the whole array at once
I'm trying to:
Go into a Drive folder and retrieve the spreadSheetIDs of existing spreadsheets
Go into each spreadsheet and get some data
Copy that data into a TargetSheet
With the condition that the data does not exist already
From 1-3 I have no issues, but I cannot correctly search and match if the data exists already.
This is my code so far. If I run the code twice on the same data set, some data is copied when it should not be, since it already exists.
Any help, please?
function getReportData() {
//Sources
var reportFolder = DriveApp.getFolderById('ReportFolderID') // Get Status reports folder
var reportsList = reportFolder.getFiles(); //Returns FileIterator object
var spreadSheets = [];
var targetSSheet = SpreadsheetApp.openById('TargetSheetID');
var targetSheet = targetSSheet.getActiveSheet();
var lastRow = targetSheet.getLastRow();
var searchRange = targetSheet.getRange(2, 1, lastRow, 2);
var searchRangeV = searchRange.getValues();
var allStatuses = [];
//Populate the reportSheets list with latest report sheet IDs
while (reportsList.hasNext()) {
var reports = reportsList.next(); //Object of type file
spreadSheets.push(reports.getId());
}
// Loop through the list of report sheets
for (i = 0; i < spreadSheets.length; i++) {
var spreadSheet = SpreadsheetApp.openById(spreadSheets[i]);
var activeSheet = spreadSheet.getActiveSheet();
var individualStatus = [];
// Gets project report data
var projectName = activeSheet.getRange("B3:B4").getValue();
var reportDate = activeSheet.getRange("I3:I4").getValue();
var projectStatus = activeSheet.getRange("G9:i9").getValue();
// Creates reports array
individualStatus.push(reportDate, projectName, projectStatus);
allStatuses.push(individualStatus)
}
//Cleans the status array of existing ones
for (j = 0; j < allStatuses.length; j++) {
var searchKey = allStatuses[j][0] + allStatuses[j][1];
searchKey
Logger.log(searchKey)
for (k = 0; k < searchRangeV.length; k++) {
var matchKey = searchRangeV[k][0] + searchRangeV[k][1];
if (searchKey == matchKey) {
allStatuses.splice(j, 1)
break;
} else {
Logger.log(searchKey)
Logger.log(matchKey)
}
}
}
//Copies the data to Target Sheet
for (var project = 0; project < allStatuses.length; project++) {
//Gets the last row each time it goes through the loop
var latestRow = targetSheet.getLastRow();
var lastColumn = targetSheet.getLastColumn();
for (var status = 0; status < allStatuses[project].length; status++) {
targetSheet.getRange(latestRow + 1, status + 1, 1, 1).setValue(allStatuses[project][status])
}
}
}
I'm getting HTML from a forum url, and parsing the post count of the user from their profile page. I don't know how to write the parsed number into the Google spreadsheet.
It should go account by account in column B till last row and update the column A with count.
The script doesn't give me any errors, but it doesn't set the retrieved value into the spreadsheet.
function msg(message){
Browser.msgBox(message);
}
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu("Update")
.addItem('Update Table', 'updatePosts')
.addToUi();
}
function getPostCount(profileUrl){
var html = UrlFetchApp.fetch(profileUrl).getContentText();
var sliced = html.slice(0,html.search('Posts Per Day'));
sliced = sliced.slice(sliced.search('<dt>Total Posts</dt>'),sliced.length);
postCount = sliced.slice(sliced.search("<dd> ")+"<dd> ".length,sliced.search("</dd>"));
return postCount;
}
function updatePosts(){
if(arguments[0]===false){
showAlert = false;
} else {
showAlert=true;
}
var spreadSheet = SpreadsheetApp.getActiveSpreadsheet();
var accountSheet = spreadSheet.getSheetByName("account-stats");
var statsLastCol = statsSheet.getLastColumn();
var accountCount = accountSheet.getLastRow();
var newValue = 0;
var oldValue = 0;
var totalNewPosts = 0;
for (var i=2; i<=accountCount; i++){
newValue = parseInt(getPostCount(accountSheet.getRange(i, 9).getValue()));
oldValue = parseInt(accountSheet.getRange(i, 7).getValue());
totalNewPosts = totalNewPosts + newValue - oldValue;
accountSheet.getRange(i, 7).setValue(newValue);
statsSheet.getRange(i,statsLastCol).setValue(newValue-todaysValue);
}
if(showAlert==false){
return 0;
}
msg(totalNewPosts+" new post found!");
}
function valinar(needle, haystack){
haystack = haystack[0];
for (var i in haystack){
if(haystack[i]==needle){
return true;
}
}
return false;
}
The is the first time I'm doing something like this and working from an example from other site.
I have one more question. In function getPostCount I send the function profileurl. Where do I declare that ?
Here is how you get the URL out of the spreadsheet:
function getPostCount(profileUrl){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var thisSheet = ss.getSheetByName("List1");
var getNumberOfRows = thisSheet.getLastRow();
var urlProfile = "";
var sliced = "";
var A_Column = "";
var arrayIndex = 0;
var rngA2Bx = thisSheet.getRange(2, 2, getNumberOfRows, 1).getValues();
for (var i = 2; i < getNumberOfRows + 1; i++) { //Start getting urls from row 2
//Logger.log('count i: ' + i);
arrayIndex = i-2;
urlProfile = rngA2Bx[arrayIndex][0];
//Logger.log('urlProfile: ' + urlProfile);
var html = UrlFetchApp.fetch(urlProfile).getContentText();
sliced = html.slice(0,html.search('Posts Per Day'));
var postCount = sliced.slice(sliced.search("<dd> ")+"<dd> ".length,sliced.search("</dd>"));
sliced = sliced.slice(sliced.search('<dt>Total Posts</dt>'),sliced.length);
postCount = sliced.slice(sliced.search("<dd> ")+"<dd> ".length,sliced.search("</dd>"));
Logger.log('postCount: ' + postCount);
A_Column = thisSheet.getRange(i, 1);
A_Column.setValue(postCount);
};
}
You're missing var in front of one of your variables:
postCount = sliced.slice(sliced.search("<dd> ")+"<dd> ".length,sliced.search("</dd>"));
That won't work. Need to put var in front. var postCount = ....
In this function:
function updatePosts(){
if(arguments[0]===false){
showAlert = false;
} else {
showAlert=true;
}
There is no array named arguments anywhere in your code. Where is arguments defined and how is it getting any values put into it?