I am using a script to automate form generation.
It basically loops over a list of elements, since each of them should have a different forms link.
How can I store the links generated corresponding to each group in a google spreadsheet?
I would like to have a spreadsheet in this way:
Group Link
138 https://docs.google.com/forms/idForm138
139 https://docs.google.com/forms/idForm139
Here's my code:
var lista_url=[]
var group=[137, 138, 139]
function createForm(group) {
// create & name Form
var item = "Speaker Information Form";
var form = FormApp.create(item)
.setTitle(item);
// single line text field
item = group;
form.addTextItem()
.setTitle(item)
.setRequired(true);
// multi-line "text area"
item = "Short biography (4-6 sentences)";
form.addParagraphTextItem()
.setTitle(item)
.setRequired(true);
// radiobuttons
item = "Handout format";
var choices = ["1-Pager", "Stapled", "Soft copy (PDF)", "none"];
form.addMultipleChoiceItem()
.setTitle(item)
.setChoiceValues(choices)
.setRequired(true);
// (multiple choice) checkboxes
item = "Microphone preference (if any)";
choices = ["wireless/lapel", "handheld", "podium/stand"];
form.addCheckboxItem()
.setTitle(item)
.setChoiceValues(choices);
var url_form= Logger.log('Published URL: ' + form.getPublishedUrl());
Logger.log('Group: '+group)
lista_url.push(url_form)
}
function generate_Form_links(group){
group.forEach(function(item, index){
console.log(item, index)
createForm(item)
}
}
generate_Form_links(group)
EDIT:
Implementing this raises this error: TypeError: infoArray.join is not a function
function excelformat(lista_url) {
var result_table = lista_url
var lineArray = [];
result_table.forEach(function(infoArray, index) {
var line = infoArray.join(" \t");
lineArray.push(index == 0 ? line : line);
});
var csvContent = lineArray.join("\r\n");
var excel_file = document.createElement('a');
excel_file.setAttribute('href', 'data:application/vnd.ms-excel;charset=utf-8,' + encodeURIComponent(csvContent));
excel_file.setAttribute('download', 'Visitor_History.xls');
document.body.appendChild(excel_file);
excel_file.click();
document.body.removeChild(excel_file);
}
excelformat(lista_url)
Not sure if I understand your task correctly. So, if you have the two arrays: group and links you can add their content in spreadsheet this way:
var group = [138, 139];
var links = ['https://docs.google.com/forms/idForm138','https://docs.google.com/forms/idForm139'];
function save_arrays_to_spreadsheet(group, links) {
var ss = SpreadsheetApp.openById(ss_id); // <-- put the spreadsheet ID here
var sheet = ss.getSheets()[0]; // let it be a first sheet
for (var i in group) sheet.appendRow([group[i], links[i]]); // add a row
}
And I don't understand what this function in your code is doing:
function generate_Form_links(group){
for (var group=0; group<=group.length ; group=i++){
createForm(group) //call our function for each value in list
}
}
It gets group (an array?) and converts it into zero var group = 0, and then there appears the variable i, etc. It all looks like an error to me.
Probably, I don't know, there should be:
function generate_Form_links(group) {
for (var i=0; i<=group.length; i++) { createForm(group[i]) }
}
Related
I am very new to Google Apps Scripts, but am working to create a spreadsheet function that will create new tabs based a list of names (in cells C6:C). Some of the cells may not have values in them, so would like to exclude these cells from creating new tabs.
The function works as planned, but after running the script, the new tabs are named with a (1), (2), (3),... etc. after the name that is in the list. For example if the first name in the list is "First Tab", the new tab gets named "First Tab (1)" after my current script is run.
I'm not sure as to what would be causing the numbers in the tab names, so any help would be greatly appreciated!
For reference, Cell B4 contains a count of the number of names in the List that the new Tabs should be created from
'''
function createProjectTabs() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var range = ss.getRange("C6:C");
var values = range.getValues().filter(String);
var total = ss.getRange('B4').getValue();
for(var k=0; k < total; k++){
try{
var tabName = range[k][0];
ss.insertSheet(tabName);}
catch(err){
tabName = values [k][0]+ " " + k;
ss.insertSheet(tabName);}
}
}
function createNonDuplicateProjectTabs() {
var ss = SpreadsheetApp.getActive();
const shts = ss.getSheets().map(s => s.getName());
var sh = ss.getSheets()[0];
var vs = ss.getRange(6,3,sh.getLastRow()-5,1).getValues().flat();
vs.forEach(n => {
if(!~shts.indexOf(n)) {
ss.insertSheet(n)
}
});
}
To get this specific function to work, I'm trying it out in a simple test sheet.
I've got two sheets(STATUS and FEBRUARI) in the FEBRUARI sheet I've selected a certain cell. This cell has a value. What I want the script to do is to look at that value, find that value in the STATUS sheet(say it finds it in A1) and return the value in B1 to a cell note in the selected cell in the FEBRUARI sheet. As example: in the cell it says "Project 6" and the cell-note gives info about this project.
This is what I got. This gives me a certain value(-1) but it doesn't seem to matter where i put the lookupvalue.. it always returns -1.
// My Script
function noteSetter() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var lookupvalue = SpreadsheetApp.getActiveSheet().getActiveCell().getValue();
var sheet = ss.getSheetByName("STATUS"); //source sheet
var sheet2 = ss.getSheetByName("FEBRUARI"); //result sheet
var cellnote = SpreadsheetApp.getActiveSheet().getActiveCell();
var lc = sheet.getLastColumn()
var lookup = sheet.getRange(1,1,1,lc).getValues() //
var index = lookup.indexOf(lookupvalue)
cellnote.setNote(index);
// This part will actually run the script once it's up and running
function onOpen() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [{
name : "Set cell note",
functionName : "noteSetter"
}];
sheet.addMenu("Scripts", entries);
};
}
var lookup = sheet.getRange(1,1,1,lc).getValues();
var index = lookup.indexOf(lookupvalue)
The first line returns a 2D array. indexOf() only works on flattened arrays. Try using:
var lookup = sheet.getRange(1,1,1,lc).getValues()[0];
According to Google preferably use getCurrentCell() instead of getActiveCell() because it returns the current highlighted (or selected) cell.
Also your onOpen() function should be outside of your noteSetter() function otherwise it is not being called when the spreadsheet opens.
The following code will do what you want for a sheet like yours. If the data order is altered you have to alter the range formulas accordingly.
/*
* This function will run when the Spreadsheet is opened and,
* will add a Menu item for the noteSetter function
*/
function onOpen() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [{
name : "Set cell note",
functionName : "noteSetter"
}];
sheet.addMenu("My Menu", entries);
};
function noteSetter() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("STATUS"); //source sheet
var sheet2 = ss.getSheetByName("FEBRUARI"); //result sheet
var noteCell = sheet2.getCurrentCell();
var lookUpValue = noteCell.getValue();
// Search through Col C in "STATUS" sheet to get the matching row
// You need to transpose the lookUpRange
var lookUpRange = sheet.getRange(2,3,sheet.getDataRange().getLastRow(),1).getValues();
lookUpRange = transpose(lookUpRange);
var index = lookUpRange[0].indexOf(lookUpValue); // Starts at 0
var row = index + 2; // One for the omitted header and one because rows start at 1
var note = sheet.getRange(row,7).getValue();
noteCell.setNote(note);
}
// You need to transpose to avoid looping through the array
function transpose(a)
{
return Object.keys(a[0]).map(function (c) { return a.map(function (r) { return r[c]; }); });
}
I'm doing a data transfer of several spreadsheets to a single one, what I do is transfer the last data of certain columns to the master spreadsheet and also insert them in the last available row of certain columns, for now, I insert all the data but I would like to to know how I can have it examine the master spreadsheet so that if those data already exist, it does not delete them but update them. The script that I have is the following ...
function Gas10(){
var ss1 = SpreadsheetApp.openById("ID");
var ssh1 = ss1.getSheetByName("Sheet 1");
var lastRow1 = ssh1.getLastRow();
var gtRange1 = ssh1.getRange("C"+(lastRow1)+":K"+(lastRow1)).getValues();
var gtRange2= ssh1.getRange("A" + (lastRow1)).getValue();
var ss2 = SpreadsheetApp.getActiveSpreadsheet();
var ssh2 = ss.getSheetByName("Sheet 2");
var lastRow2 = ssh2.getLastRow() + 1;
var setRange1 = ssh2.getRange(lastRow2, 4, gtRange1.length, gtRange1[0].length).setValues(gtRange1);
var setRange2 = ssh2.getRange(lastRow2, 3).setValue(gtRange2);
}
I need to know how I can do it when I insert a piece of information (I already do that), but update it if it already exists. This is the example that I created so that it can be better understood, in this example I have two sheets of which from sheet 1 I pass data to sheet 2 and what I'm looking for is that sheet 2 updates all the data that are equal to (Name, Num, Proyect). I hope that now I understand better what I'm looking for.
Basically what you have to do is
get the new Line you want to add to the destination spreadsheet
get all the required datas of the destination spreadsheet
Check if the new Line datas have the same datas than in the destination data array
If so change ID value
paste changed datas in the destination spreadsheet
based on this spreadsheet The code should look something like this
function Gas10(){
var ss1 = SpreadsheetApp.getActiveSpreadsheet();
var ssh1 = ss1.getSheetByName("Sheet 1");
var ssh2 = ss1.getSheetByName("Sheet 2");
var lastRow1 = ssh1.getLastRow();
var lastCol1 = ssh1.getLastColumn();
var newLine = ssh1.getRange(lastRow1, 2, 1, lastCol1 - 1 ).getValues();
var destDatas = ssh2.getDataRange().getValues();
for (var i = 1; i < destDatas.length; i++)
{
if (newLine[0][0] == destDatas[i][0]
&& newLine[0][1] == destDatas[i][1]
&& newLine[0][2] == destDatas[i][2])
{
destDatas[i][3] = newLine[0][3];
}
}
// add newLine to destDatas
destDatas.splice(destDatas.length, 0, newLine[0]);
var lastColumn = ssh2.getLastColumn();
var lastRow2 = ssh2.getLastRow() + 1;
ssh2.getRange(1, 1, destDatas.length, lastColumn).setValues(destDatas);
ssh1.deleteRow(lastRow1);
}
Here's an example I played around with:
It looks at the slave sheet for any data. When it finds data it puts the row and col and value into an obj which is then added to an array. When it finishes it calls the updMaster which then looks for data in those same cells (assuming that the cells are in the same place if those cells are blank then it adds data and I also changed the background to lightblue to show me where it updated the cells.
You could run the getSlaveData() for different sheets if you wish.
function getSlaveData(){
var ss=SpreadsheetApp.getActive();
var ssh=ss.getSheetByName('Sheet2');
var sA=[];
var srg=ssh.getDataRange();
var svA=srg.getValues();
for(var i=0;i<svA.length;i++){
for(var j=0;j<svA[i].length;j++){
//if(svA[i][j]){
if(!ssh.getRange(i+1,j+1).isBlank()){//optional way to look for values
var sObj={};
sObj['row']=i + 1;
sObj['col']=j + 1;
sObj['value']=svA[i][j];
sA.push(sObj);
}
}
}
updMaster(sA);
}
function updMaster(sA){
var ss=SpreadsheetApp.getActive();
var msh=ss.getSheetByName('Sheet1');
for(var i=0;i<sA.length;i++){
if(msh.getRange(sA[i].row,sA[i].col).isBlank()){
msh.getRange(sA[i].row,sA[i].col).setValue(sA[i].value);
msh.getRange(sA[i].row,sA[i].col).setBackground('lightblue');
}
}
}
I am trying to search, find and fetch some data from a CSV file using HTML/PHP/Javascript.
I want to make a form with 2 dropdowns, one for the FROM Zone name and one for the TO Zone name, and use the Zone codes (102, 104, 105 etc) as values for the dropdown items.
After the user have selected the FROM and TO i want to display the single digit to the far right (col 5).
Example: User choose "Zone1" to "Zone4", then the number "4" should be returned.
FromZoneCode;FromZoneName;ToZoneCode;ToZoneName;Distance;;;;
101;zone1;101;zone1;1;;;;
101;zone1;104;zone4;4;;;;
101;zone1;105;zone5;5;;;;
104;zone4;101;zone1;4;;;;
104;zone4;105;zone5;2;;;;
104;zone4;104;zone4;1;;;;
I have tried to search for a solution for this but i cant seem to find the right info.
Worked out after a long time:
Don't know how you got the CSV data. In the following example, I got it by an ajax request.
No jQuery needed.
Created the dropdowns dynamically.
Set the variable delimeter to ; (or) , as required, because most CSV files contains CSV delimeter.
Give the names of the columns for which dropdowns to be created in the variables dropdownname1 and dropdownname2.
Give the name of the column to be displayed as result on chaning dropdowns in the variable resultname.
Create a <span> element with id="result" in the HTML to display the result.
Variable keys contains column names.
Variable values contains array of arrays as values.
var data = [];
$.ajax({
url:"/Users/Default/Downloads/test.csv",
type:"GET",
datatype:"csv",
success:function(response){
data = response.split(/\r\n/);
start();
}
});
//Logic starts here
//Initializations
var keys = [], values = [], delimiter = ";";
var dropdownname1 = "FromZoneName", dropdownname2 = "ToZoneName", resultname = "Distance";
var resultelem = document.getElementById("result");
//Functionalities
function createDropdown(field)
{
function createOption(option, isselected)
{
var optionelem = document.createElement("option");
optionelem.value=option;
optionelem.text=option;
optionelem.selected = isselected;
return optionelem;
}
var selectelem = document.createElement("select");
selectelem.setAttribute("id",field);
var insertedoptions = [];
for(var i=0;i<values.length;i++)
{
var option = values[i][keys.indexOf(field)];
if(insertedoptions.indexOf(option) == -1)
{
insertedoptions.push(option);
selectelem.appendChild(createOption(option));
}
}
selectelem.appendChild(createOption("",true));
return selectelem;
}
function start()
{
keys = data.splice(0,1)[0].split(delimiter);
values = [];
for(var i=0,n=data.length;i<n;i++)
{
values.push(data[i].split(delimiter));
}
var bodyelem = document.getElementsByTagName("body")[0];
bodyelem.appendChild(createDropdown(dropdownname1));
bodyelem.appendChild(createDropdown(dropdownname2));
document.getElementById(dropdownname1).addEventListener("change",displayData);
document.getElementById(dropdownname2).addEventListener("change",displayData);
}
function displayData()
{
var selectelem1 = document.getElementById(dropdownname1), selectelem2 = document.getElementById(dropdownname2);
var selectedvalue1 = selectelem1.value, selectedvalue2 = selectelem2.value;
for(var i=0,n=values.length;i<n;i++)
{
if(values[i][keys.indexOf(dropdownname1)] == selectedvalue1 && values[i][keys.indexOf(dropdownname2)] == selectedvalue2)
{
resultelem.innerHTML=values[i][keys.indexOf(resultname)];
break;
}
else
{
resultelem.innerHTML="";
}
}
}
I am attempting to send an array through the callback and having no luck. Let me explain my intent and perhaps an expert out there can send me some ideas for how to solve this dilema.
I have creates a spreadsheet that collects data. I then have a UI script which pulls row data into a flex table for a user to process by clicking a checkbox. I created a separate flex table that contains the checkboxes which the user checks or leaves blank. In my script I need to send an array that contains the checkbox condition. Why? Because I also need the row # or array placement that I can push to the spreadsheet to send the correct updated status for the data.
The script only pulls data that needs action into the UI. Thus I may be acting on Rows 1,3,4,5,and 8 of the spreadsheet but on the UI flextable the row that correspond to the data are rows 1,2,3,4,5 thus the row assignments don't match. BUT if I use an array I can capture that the row pulled was 1,3,4,5,and 8 and then update the spreadsheet accordingly.
However, that is the problem when I try to callback my array labeled offset[inc] I cannot get it to work I get run errors of cannot find method addcallback and cannot get it to work.
Recommendations on how to send an array through the addcallback method or alternate recommendations would be appreciated.
Thanks,
Sean Nutzman
function doGet(e){
var app = UiApp.createApplication();
//Create Caption Panel
var captionPanel = app.createCaptionPanel('Detention Attendance').setWidth('350px').setHeight('75px').setStyleAttribute('fontWeight', 'bold').setStyleAttribute('fontSize', '24px');
//Add a widget to caption panel
captionPanel.add(app.createLabel("Please enter attendance for Detention by clicking the checkbox next to the student's name if they were present. Then click Sumbit."));
//add the caption panel to the application
app.add(captionPanel);
var panel = app.createHorizontalPanel();
var flexTable = app.createFlexTable().setStyleAttribute('border', '2px solid black')
.setStyleAttribute('borderCollapse','collapse')
.setBorderWidth(2)
.setCellSpacing(50)
.setCellPadding(6);
//Get Data from spreadsheet
var spreadsheetId = '0Aup0nXQ4K-pydFREb1FFcTFYX3lOenNQenR1Q01jQ1E'; //Change this to the Spreadsheet ID
var dataArray = getData(spreadsheetId);
var inc = 1;
//Load data into table cells
for (var row = 0; row<dataArray.length; row++) {
var booleanCheck = dataArray[row] [17];
var offset = new Array();
if (booleanCheck == "" || booleanCheck == "Date Served") {
if (row > 0) {
Logger.log("Row value = " + row);
var ticketDate = dataArray[row] [0];
var dateStamp = Utilities.formatDate(new Date(ticketDate), "America/Chicago", "MM/dd/yyyy");
dataArray[row] [0] = dateStamp;
var ticketDate2 = dataArray[row] [16];
var dateStamp2 = Utilities.formatDate(new Date(ticketDate2), "America/Chicago", "MM/dd/yyyy");
dataArray[row] [16] = dateStamp2;
flexTable.setText(row, 1, dataArray[row][2].toString());
flexTable.setText(row, 0, dataArray[row][0].toString());
flexTable.setText(row, 2, dataArray[row][16].toString());
offset[inc] = row; inc++;
Logger.log('Inc variable = ' + inc);
Logger.log('Offset = ' + offset[inc-1]);
} else {
Logger.log("Inside ELSE row is not > 0");
Logger.log("Row value here = " + row);
flexTable.setText(0, 1, "Student's Name").setStyleAttribute(0, 1, 'fontWeight', 'bold');
flexTable.setText(0, 0, "Date Assigned").setStyleAttribute(0, 0, 'fontWeight', 'bold');
flexTable.setText(0, 2, "Date Delinquent").setStyleAttribute(0, 2, 'fontWeight', 'bold');
}
}
}
Logger.log(offset);
panel.add(flexTable);
var check1 = app.createCheckBox().setName('ch1');
var check2 = app.createCheckBox().setName('ch2');
var check3 = app.createCheckBox().setName('ch3');
var check4 = app.createCheckBox().setName('ch4');
var check5 = app.createCheckBox().setName('ch5');
var check6 = app.createCheckBox().setName('ch6');
var check7 = app.createCheckBox().setName('ch7');
var check8 = app.createCheckBox().setName('ch8');
var check9 = app.createCheckBox().setName('ch9');
var submitButton = app.createButton("Submit");
var handler = app.createServerClickHandler('updateStatus');
handler.addCallbackElement(check1)
.addCallbackElement(check2)
.addCallbackElement(check3)
.addCallbackElement(check4)
.addCallbackElement(check5)
.addCallbackElement(check6)
.addCallbackElement(check7)
.addCallbackElement(check8)
.addCallbackElement(check9)
.addCallbackElement(offset);
submitButton.addClickHandler(handler);
handler.addCallbackElement(check1)
.addCallbackElement(check2)
.addCallbackElement(check3)
.addCallbackElement(check4)
.addCallbackElement(check5)
.addCallbackElement(check6)
.addCallbackElement(check7)
.addCallbackElement(check8)
.addCallbackElement(check9)
.addCallbackElement(offset);
var table = app.createGrid(11,1).setStyleAttribute('border', '2px solid black')
.setStyleAttribute('borderCollapse','collapse')
.setBorderWidth(2)
.setWidth('75px')
.setCellSpacing(5)
.setCellPadding(6);
table.setStyleAttributes({textAlign: "center"});
table.setStyleAttribute('fontWeight', 'bold').setText(0, 0, 'Attendance');
table.setWidget(1,0, (check1));
table.setWidget(2,0, (check2));
table.setWidget(3,0, (check3));
table.setWidget(4,0, (check4));
table.setWidget(5,0, (check5));
table.setWidget(6,0, (check6));
table.setWidget(7,0, (check7));
table.setWidget(8,0, (check8));
table.setWidget(9,0, (check9));
table.setWidget(10,0,(submitButton));
panel.add(table);
app.add(panel);
app.close();
return app;
}
What I usually do is to convert the array to a string and write it on the widget's tag.
Then I can retrieve it using e.parameter.widgetName_tag in the handler function. At this point I can split it to get back the array : e.parameter.widgetName_tag.split(',');
You'll have to be careful when choosing the join and split character since your data might contain a comma (which is the default separator in arrays)... I often use a | or any other 'uncommon' character (Ë,Í;∆) in combination with join('∆') and split('∆') so I'm sure I get the array back as it should.
Of course the widget must be included in the callBackElement but this is easily achieved by using the highest level parent UiApp element as callBackElement.
Last comment : try to use widget Ids that will simplify your life ... for example use Ids containing a number that corresponds to the array index ( chk0, chk1, chk2...) so that you can easily retrieve the numeric value to use in your handler function using something like this :
Number(e.parameter.source.replace(/[a-z]/ig,''))
which will give you a number that identifies which checkBox is the origin of the handler call so you can write :
var arrayElement = e.parameter.widgetName_tag.split(',')[Number(e.parameter.source.replace(/[a-z]/ig,''))];
var array = ['foo','poo'];
var arrayString = JSON.stringify(array);
At that point, just attach arrayString to a callback element and voila! Then in the handlerFunction, you access it out with e.parameter.arrayString and then parse it to return it back to an array like so:
var array = JSON.parse(e.parameter.arrayName);
//array = ['foo','poo']