Google forms list items with dynamic goto section - google-apps-script

I found this answer posted here earlier.
function GoToPage() {
var form = FormApp.openById('MY FORM');
var spreadsheet = SpreadsheetApp.openById("MY SPREADSHEET");
var sheet = spreadsheet.getSheetByName("MY SHEET");
var list = form.getItemById("MY MULTIPLE CHOICE ID").asListItem()
var pagebreak01 = form.getItemById("PAGE BREAK ID 1").asPageBreakItem();
var pagebreak02 = form.getItemById("PAGE BREAK ID 2").asPageBreakItem();
var pagebreak03 = form.getItemById("PAGE BREAK ID 3").asPageBreakItem();
var choice1 = sheet.getRange("RANGE 1 FROM MY SHEET").getValues();
var choice2 = sheet.getRange("RANGE 2 FROM MY SHEET").getValues();
var choice3 = sheet.getRange("RANGE 3 FROM MY SHEET").getValues();
list.setChoices([
list.createChoice(choice1, pagebreak01),
list.createChoice(choice2, pagebreak02),
list.createChoice(choice3, pagebreak03)]);
}
when using a range larger than 1 in the choice# variables, this makes just one choice with all the items in the range separated by commas. how can I use this to make a list of choices that has a dynamic range?
My exact choices should be:
"** Add Location **" , goto newLocation section of form.
location 1, goto next section.
location 2, goto next section.
location 3, ect....
If you select new location and fill out the form to add a new location the script then adds that location name to the choices for the next time you fill out the form. so this list is continually growing larger.
function updateForm() {
var locationForm = form.getItemById("413015265").asListItem();
var equipmentForm = form.getItemById("2123556695").asListItem();
var newLocation = form.getItemById('231469190').asPageBreakItem();
var newItem = form.getItemById('90044295').asPageBreakItem();
var gotoIssue = form.getItemById('1493817332').asPageBreakItem();
var gotoLocation = form.getItemById('1425783734').asPageBreakItem();
var locations = tblLocations.getRange(2,2,tblLocations.getLastRow()-1,1).getValues();
var items = tblItems.getRange(2,2,tblItems.getLastRow()-1,1).getValues();
items = items.sort();
locations = locations.sort();
locationForm.setChoices([
locationForm.createChoice("** Add Location **",newLocation),
locationForm.createChoice(locations,gotoIssue)
]);
equipmentForm.setChoices([
equipmentForm.createChoice("** Add Item **",newItem),
equipmentForm.createChoice(items,gotoLocation)
]);
}
after running the above script I ended up with only 2 choices. 1 new location/item entry which works correctly, and 1 entry with all the current locations/items in the spreadsheet already, separated by commas. Can i just run a foreach loop inside the setChoices command?

function updateForm() {
var locationForm = form.getItemById("413015265").asListItem();
var equipmentForm = form.getItemById("2123556695").asListItem();
var newLocation = form.getItemById('231469190').asPageBreakItem();
var newItem = form.getItemById('90044295').asPageBreakItem();
var gotoIssue = form.getItemById('1493817332').asPageBreakItem();
var gotoLocation = form.getItemById('1425783734').asPageBreakItem();
var locations = tblLocations.getRange(2,2,tblLocations.getLastRow()-1,1).getValues();
var items = tblItems.getRange(2,2,tblItems.getLastRow()-1,1).getValues();
var locationChoices = [];
var itemChoices = [];
locationChoices[0] = locationForm.createChoice("** Add Location **",newLocation);
itemChoices[0] = equipmentForm.createChoice("** Add Item **",newItem);
items = items.sort();
locations = locations.sort();
for (var i=0;i<tblLocations.getLastRow()-1;i++){
locationChoices[i+1]= locationForm.createChoice(locations[i],gotoIssue);
}
locationForm.setChoices(locationChoices);
for (var i=0;i<tblItems.getLastRow()-1;i++){
itemChoices[i+1]= equipmentForm.createChoice(items[i],gotoLocation);
}
equipmentForm.setChoices(itemChoices);
}
I was able to figure it out. It's probably not the most beautiful solution but it works. :)

Related

How can I use a variable as a parameter in getSheetByName?

I have the following code which generates some gslides slides from data in a spreadsheet. The code is attached to the gslides file and it works when the parameters for link/year are entered as strings. However, these parameters will sometimes vary from a set list (e.g. pull from a different sheet or from a different file) so the code sometimes needs to be edited.
I want to share this with colleagues who don't know how to use app scripts and so tried to edit it so that the link/year can be pulled from a textbox in one of the slides in the gslides file. They are pulling through ok but when I try to use it as a parameter I get an error for line 11 (var values = sheet.getRange('F2:aa2').getValues();) as the sheet returns as null.
I've tried wrapping it in quotes (" ' " + year + " ' ") but that doesn't work either.
Please let me know if I need to include more info or a link to the sheets/slides files.
function generateStarters() {
var deck = SlidesApp.getActivePresentation();
var slides = deck.getSlides();
var link = slides[2].getPageElements()[2].asShape().getText().asString()
var year = slides[2].getPageElements()[4].asShape().getText().asString()
var dataSpreadsheetUrl = link
var ss = SpreadsheetApp.openByUrl(dataSpreadsheetUrl);
var sheet = ss.getSheetByName(year);
var values = sheet.getRange('F2:aa2').getValues();
var templateSlide = slides[1];
var presLength = slides.length;
values.forEach(function(page){
if(page[0]){
var Q1 = page[0];
var A1 = page[1];
var Q2 = page[4];
var A2 = page[5];
var Q3 = page[8];
var A3 = page[9];
var Q4 = page[12];
var A4 = page[13];
var QK = page[16];
var AK = page[17];
var QF = page[20];
var AF = page[21];
templateSlide.duplicate(); //duplicate the template page
slides = deck.getSlides(); //update the slides array for indexes and length
newSlide = slides[3]; // declare the new page to update
var shapes = (newSlide.getShapes());
shapes.forEach(function(shape){
shape.getText().replaceAllText('{{q1}}',Q1);
shape.getText().replaceAllText('{{a1}}',A1);
shape.getText().replaceAllText('{{q2}}',Q2);
shape.getText().replaceAllText('{{a2}}',A2);
shape.getText().replaceAllText('{{q3}}',Q3);
shape.getText().replaceAllText('{{a3}}',A3);
shape.getText().replaceAllText('{{q4}}',Q4);
shape.getText().replaceAllText('{{a4}}',A4);
shape.getText().replaceAllText('{{qk}}',QK);
shape.getText().replaceAllText('{{ak}}',AK);
shape.getText().replaceAllText('{{qf}}',QF);
shape.getText().replaceAllText('{{af}}',AF);
});
presLength = slides.length;
newSlide.move(presLength);
}
});
} ```
The string from the slides file seems to always have a space at the end (from the textbox) so I just needed to add an extra line of code to remove that space.

Google Apps Scrollpanel not updating with information

My script is iterating through some spreadsheet information. I read the documentation and the scrollpanel is only allowed one child. I therefore wrapped the horizontal panel information inside a scrollpanel but for some reason the scrollpanel never shows up with contents just the colored background. Any ideas why this may be?
var myscrollpanel = app.createScrollPanel().setPixelSize(100, 100);
myscrollpanel.setWidth("100%");
myscrollpanel.setStyleAttribute("background", "silver");
var vpanel = app.createVerticalPanel();
for (i=1; i <= mylastrow;++i)
{
var cellsname= mydatarange.getCell(i,1).getValue().toLowerCase();
// Browser.msgBox(cellsname );
// Browser.msgBox(searchstr.toString());
if (cellsname.toString() == searchstr.toString())
{
var panelrow = app.createHorizontalPanel();
var ddate = app.createTextBox();
var sbehavior = app.createTextBox();
var sconsequence = app.createTextBox();
var scomment = app.createLabel();
var steacher = app.createTextBox();
ddate.setText(mydatarange.getCell(i,5).getValue());
sbehavior.setText(mydatarange.getCell(i,8).getValue());
sconsequence.setText(mydatarange.getCell(i,6).getValue());
scomment.setText(mydatarange.getCell(i,10).getValue());
steacher.setText(mydatarange.getCell(i,7).getValue());
panelrow.add(ddate);
panelrow.add(sbehavior);
panelrow.add(sconsequence);
panelrow.add(steacher);
panelrow.add(scomment);
vpanel.add(panelrow);
app.add(panelrow);
cnt = ++cnt;
}
}
// Browser.msgBox(cnt);
myscrollpanel.add(vpanel);
app.add(myscrollpanel);
// return myscrollpanel;
return app ;// update UI
there are a few "anomalies" in your code that could cause some issues, I suggest a couple of changes (see code below).
Another point is that it is not a good idea to read a spreadsheet in a loop, it is mush more efficient to read the whole range in one time and then use the array values to iterate.
Here is a code proposal, I didn't test it (and it will probably need some debugging) but put a few comments to explain the changes.
var myscrollpanel = app.createScrollPanel()//.setPixelSize(100, 100);// 100 pixel is quite small to put all the items you add !!
myscrollpanel.setWidth("100%");//
myscrollpanel.setStyleAttribute("background", "silver");// this will not affect the widgets you add to the panel...
var vpanel = app.createVerticalPanel();
var data = mydatarange.getValues();// get all data in an array
for (i=0; i <data.length;++i) // and work directly with array values (indexed to 0 instead of cells indexed to 1)
{
var cellsname= data[i][0].toString().toLowerCase();
if (cellsname == searchstr.toString()){
var panelrow = app.createHorizontalPanel();
var ddate = app.createTextBox();
var sbehavior = app.createTextBox();
var sconsequence = app.createTextBox();
var scomment = app.createLabel();
var steacher = app.createTextBox();
ddate.setText(data[i][4].getValue());
sbehavior.setText(data[i][7].getValue());
sconsequence.setText(data[i][5].getValue());
scomment.setText(data[i][9].getValue());
steacher.setText(data[i][6].getValue());
panelrow.add(ddate);
panelrow.add(sbehavior);
panelrow.add(sconsequence);
panelrow.add(steacher);
panelrow.add(scomment);
vpanel.add(panelrow);// add only to the panel, not to the app or the row will be outside the panel
cnt = ++cnt;
}
}
myscrollpanel.add(vpanel);
app.add(myscrollpanel);
return app ;// update UI
}

Google Script Error - Cannot find method add

I have been puzzled by this error. I have not been able to figure out what I am missing. Without the calls to headPanel the UI was working but would not insert the title or text label so I added the Horizontal panel in hopes of adding the text, but alas error. Please help me with this problem and I need to know what I am messing up.
function doGet(e){
var app = UiApp.createApplication();
var headPanel = app.createHorizontalPanel().setId('headPanel');
var header = app.setTitle('Detention Attendance');
var label = app.createLabel("Please enter attendance for Detention by clicking the checkbox next to the student's name if they were present. Then click Sumbit.");
headPanel.add(header); //* ERROR OCCURS HERE
headPanel.add(label);
app.add(pane);
var panel = app.createVerticalPanel();
var flexTable = app.createFlexTable().setStyleAttribute('border', '2px solid black')
.setStyleAttribute('borderCollapse','collapse')
.setBorderWidth(2);
//Get Data from spreadsheet
var spreadsheetId = 'xxxxxxxxxxxxxxxxxxxxxxxx';//Change it to your Spreadsheet ID
var dataArray = getData(spreadsheetId);
//Load data into table cells
Logger.log(dataArray);
for (var row = 0; row<dataArray.length; row++){
if (row > 0) {
var ticketDate = dataArray[row] [0];
var dateStamp = Utilities.formatDate(ticketDate, "America/Chicago", "MM/dd/yyyy");
Logger.log("dateStamp = " +dateStamp);
dataArray[row] [0] = dateStamp;
var ticketDate2 = dataArray[row] [16];
var dateStamp2 = Utilities.formatDate(ticketDate2, "America/Chicago", "MM/dd/yyyy");
dataArray[row] [16] = dateStamp2;
Logger.log("DateStamp = " +dateStamp2);
}
flexTable.setStyleAttribute("color", "purple").setText(row, 2, dataArray[row][2].toString()).setWidth('300px');
flexTable.setText(row, 0, dataArray[row][0].toString()).setWidth('600px');
flexTable.setText(row, 4, dataArray[row][16].toString()).setWidth('300px');
}
panel.add(flexTable);
app.add(panel);
return app;
}
function getData(spreadsheetId){
var ss = SpreadsheetApp.openById(spreadsheetId);
var sheet = ss.getSheets()[0].getDataRange();
Logger.log(sheet);
return sheet.getValues();
}
Your header var is a UiInstance - in fact it's the same instance as app, returned as a convenience for chaining by the call to .setTitle().
The .add() method is looking for a Widget as a parameter, and not finding it.
You don't need to keep that header var - it's only causing you trouble. In fact, you could set the Title as you create the UiApp instance:
var app = UiApp.createApplication().setTitle('Detention Attendance');

Display a pop up UI based on users' answer

I currently have a UI with drop down questions. When a user select a certain answer in the drop down list, I want to show another UI that will allow the user to answer more questions. Then, I would like to close this window and return the user to the first UI and continue answering questions.
I have tried popuppanel, set one Ui's invisibility to false but nothing seems to work.
NOTE: Since the data in my first UI are dynamic, I can't use the GUI builder so please only provide solutions using code.
These are the code I have so far:
// when user select this drop down option, show another UI
case 'Move to a special meeting':
displayActionItemsUI();
cellStatus.offset(numOfRowsOffset,0).setValue(N);
cellMeetingType.offset(numOfRowsOffset,0).setValue('Special Meeting');
break;
function displayActionItemsUI() {
var app = UiApp.getActiveApplication();
var panelActionItems = app.createVerticalPanel();
var gridActionItems = app.createGrid(3, 2).setId('gridActionItems')
.setCellPadding(15);
var lblTaskStatement = app.createLabel('Task Statement:');
var lblOwner = app.createLabel('Owner:');
var lblDeadline = app.createLabel('Deadline:');
var tboxTask = app.createTextBox().setId('tboxTask').setName('tboxTask');
var tboxOwner = app.createTextBox().setId('tboxOwner').setName('tboxOwner');
var dboxDeadline = app.createDateBox()
.setFormat(UiApp.DateTimeFormat.DATE_SHORT)
.setId('dboxDeadline').setName('dboxDeadline');
gridActionItems.setWidget(0, 0, lblTaskStatement);
gridActionItems.setWidget(0, 1, tboxTask);
gridActionItems.setWidget(1, 0, lblOwner);
gridActionItems.setWidget(1, 1, tboxOwner);
gridActionItems.setWidget(2, 0, lblDeadline);
gridActionItems.setWidget(2, 1, dboxDeadline);
var btnAdd = app.createButton('Add');
var lblTest = app.createLabel().setId('lblTest').setVisible(false);
panelActionItems.add(gridActionItems)
.add(btnAdd).add(lblTest);
app.add(panelActionItems);
addHandler = app.createServerHandler('_responseToAdd')
.addCallbackElement(panelActionItems);
btnAdd.addClickHandler(addHandler);
var ss = SpreadsheetApp.getActiveSpreadsheet();
ss.show(app);
}
// add response to a separate tab and close the UI
function _responseToAdd(e) {
// store user's inputs;
var actionItems = [];
var actionObject = new Object();
var taskStatement = e.parameter.tboxTask;
var owner = e.parameter.tboxOwner;
var deadline = e.parameter.dboxDeadline;
actionObject['task'] = taskStatement;
actionObject['owner'] = owner;
actionObject['deadline'] = deadline;
actionItems.push(actionObject);
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Action Items");
var headers = ['task', 'owner', 'deadline'];
var valuesR = ObjApp.objectToArray(headers, actionItems); //returns [[]]
sheet.getRange(sheet.getLastRow()+1, 1, 1, valuesR[0].length).setValues([valuesR[0]]);
var app = UiApp.getActiveApplication();
var panelActionItems = app.getElementById('panelActionItems');
panelActionItems.clear();
return app;
}
I think you could do it using multiple panels like in this post answer, you could build the panels and change their content depending of the answers...

Parsing XML Data that I receive from UrlFetch

I want to parse the data I get from UrlFetch into a spreadsheet, but all I'm getting is undefined can someone show me what i'm doing wrong
The xml is at the address https://dl.dropbox.com/u/11787731/Minecraft/bans.xml
function runevery15mins() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("MC Bans");
sheet.clearContents();
var banURL = "https://dl.dropbox.com/u/11787731/Minecraft/bans.xml";
var banXML = UrlFetchApp.fetch(banURL).getContentText();
var banDOC = Xml.parse(banXML, false);
var mcuser = banDOC.bans;
var x = 0;
for(var c=0; c>mcuser.length;c++){
var name = mcuser.getElement("username")[c].getText();
var date = mcuser.getElement("date")[c].getText();
var reason = mcuser.getElement("reason")[c].getText();
var duration = mcuser.getElement("duration")[c].getText();
}
sheet.appendRow([name, date, reason, duration]);
}
You have some small errors in your code.
For example, the second argument in the for loop needs to be c<mcuser.length.
Using the Xml service documentation, this worked for me
function runevery15mins() {
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("MC Bans");
sheet.clearContents();
var banURL = "https://dl.dropbox.com/u/11787731/Minecraft/bans.xml";
var banXML = UrlFetchApp.fetch(banURL).getContentText();
var banDOC = Xml.parse(banXML, false);
// Get all the child nodes from the document element that are 'user' nodes
var mcusers = banDOC.getElement().getElements('user');
for(var c=0; c<mcusers.length;c++){
var user = mcusers[c];
var name = user.getElement('username').getText();
var date = user.getElement('date').getText();
var reason = user.getElement('reason').getText();
var duration = user.getElement('duration').getText();
sheet.appendRow([name, date, reason, duration]);
}
}
Note for example that the sheet.appendRow line is INSIDE the loop, not outside as you had it before. I also deleted the X variable, since I didn't see any purpose for it.
I also created a user variable, which is an XmlElement, to make it easier to understand how to get the different contents of each node.
You were almost there.
Looks like there was another array you needed to drill down into. Also, your call back to the spreadsheet should be in the loop. Try this:
...
var mcuser = banDOC.bans.user;
for(var i in mcuser){
var name = mcuser[i].getElement("username").getText();
var date = mcuser[i].getElement("date").getText();
var reason = mcuser[i].getElement("reason").getText();
var duration = mcuser[i].getElement("duration").getText();
sheet.appendRow([name, date, reason, duration])
}