forgive me, I am new to app script. Thank you in advance.
I have a submit button form with a range of data, the destination is in another sheet. However, I need to insert the date, entity name and office in the appendRow.
The form:
DATE :
Entity Name :
Office :
ID
STOCK NO.
UNIT
DESCRIPTION
QUANTITY
1
JTL-0005
PC
BROOM, SOFT (TAMBO)
1
Output (another sheet) :
DATE
ENTITY NAME
OFFICE
ID
STOCK NO.
UNIT
DESCRIPTION
QUANTITY
1
JTL-0005
PC
BROOM, SOFT (TAMBO)
1
Above output only appends the row. How do I submit it with those values also.
Submit button code:
function btn_dataEntryPrevSubmit(){
var ui = SpreadsheetApp.getUi();
var response = ui.alert('Are you sure you want to continue?',
ui.ButtonSet.YES_NO);
var result = ui.alert('Successfully submitted request!',ui.ButtonSet.OK)
if (response == ui.Button.YES) {
const idCell = formDE.getRange("A4")
/*
let deDate = formDE.getRange("F4")
let deEntityName = formDE.getRange("F8")
let deOffice = formDE.getRange("F10")
*/
let dataEntryRange = formDE.getRange("D14:H")
let values = dataEntryRange.getValues()
for(let index = 0; index < values.length; index++){
db_RequisitionOffice.appendRow(values[index])
const nextIDCell = formDE.getRange("A4")
const nextID = nextIDCell.getValue()
values[index].unshift(nextID)
idCell.setValue(nextID+1)
nextIDCell.setValue(nextID+1)
result
}
//====================================
//DELETE VALUES IN DATA ENTRY FORM
//====================================
const dataEntryFieldRangeForDeletion = ["D14:H"]
dataEntryFieldRangeForDeletion.forEach(f => formDE.getRange(f).clearContent())
}
}
Thank you so much for your help.
Alternate Approach
You may use the map and unshift functions to adjust the output of the script to the proper columns.
Script:
You may use the following script as a basis for your own code:
function adjustData() {
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Form");
var ssOut = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Output");
var ssLastRow = ss.getLastRow();
var ssLastColumn = ss.getLastColumn()
var deDate = ss.getRange("F4").getValue();
var deEntityName = ss.getRange("F8").getValue();
var deOffice = ss.getRange("F10").getValue();
//Extract Data
var ssData = ss.getRange(14, 1, ssLastRow - 13, ssLastColumn).getValues();
ssData.map(x => {
x.unshift(deDate, deEntityName, deOffice); //Add date, entity name, and office to data
ssOut.appendRow(x); //Transfer data
});
//delete data from form
ss.getRange(14, 1, ssLastRow - 13, ssLastColumn).clear();
}
Result
Since you have not shared a sample spreadsheet, I went ahead and created a test case based on your post.
1. Form Sheet (Input)
2. Output Sheet (Output)
3. Cleared Form Sheet
Reference:
unshift()
map()
appendRow()
I'm making a google sheets app function that checks if the ID in one sheet can be associated with any of the patients (each patient receives an ID), then add it into their file (a single cell next to their name).
I'm at a point where I can get the info into the cell with .copyValuesToRange, but the problem is that all the values are copied into the cell one after another. The desired effect is that I get all values separated by ", ".
Here's my code:
function newCaseIn() {
let app = SpreadsheetApp;
let dest = app.getActiveSpreadsheet().getSheetByName("Baza Danych");
let form = app.getActiveSpreadsheet().getSheetByName("Zgloszenia");
for (let i = 2; i < 200; i++) {
if (form.getRange(i, 2).getValue()) {
while (true) {
form.getRange(i, 3).copyValuesToRange(0, 9, 9, 2, 2);
}
}
}
}
And here's how the database looks: Database FormSubmissions
NOTE: There is a form that comes down to the second sheet to allow people submit new patient files to a specified ID
It could be something like this:
function main() {
let ss = SpreadsheetApp.getActiveSpreadsheet();
let dest = ss.getSheetByName("Baza Danych");
let form = ss.getSheetByName("Zgloszenia");
// get all data from the form
var source_data = form.getDataRange().getValues();
source_data.shift(); // remove the header
// make the data object
// in: 2d array [[date,id1,doc], [date,id2,doc], ...]
// out: object {id1: [doc, doc, doc], id2: [doc, doc], ...}
var source_obj = {};
while(source_data.length) {
let [date, id, doc] = source_data.shift();
try { source_obj[id].push(doc) } catch(e) { source_obj[id] = [doc] }
}
// get all data from the dest sheet
var dest_data = dest.getDataRange().getValues();
// make a new table from the dest data and the object
var table = [];
while (dest_data.length) {
let row = dest_data.shift();
let id = row[0];
let docs = source_obj[id]; // get docs from the object
if (docs) row[8] = docs.join(', ');
table.push(row);
}
// clear the dest sheet and put the new table
dest.clearContents();
dest.getRange(1,1,table.length,table[0].length).setValues(table);
}
Update
The code from above clears existed docs in the cells of column 9 and fills it with docs from the form sheet (for relevant IDs).
If the dest sheet already has some docs in the column 9 and you want to add new docs you have to change the last loop this way:
// make a new table from the dest data and the object
var table = [];
while (dest_data.length) {
let row = dest_data.shift();
let id = row[0];
let docs = source_obj[id]; // get docs from the object
if (docs) {
let old_docs = row[8];
row[8] = docs.join(', ');
if (old_docs != '') row[8] = old_docs + ', ' + row[8];
}
table.push(row);
}
With the help of this SO questionsI am trying to scrape the following website. I would like the two teams and the time. For example, the first entry would be Chicago | Miami | 12:30 PM, and the last entry would be Colorado | Arizona | 10:10 PM. My code is as follows
function espn_schedule() {
var url = "http://www.espn.com/mlb/schedule/_/date/20180329";
var content = UrlFetchApp.fetch(url).getContentText();
var scraped = Parser.data(content).from('class="schedule has-team-logos align-left"').to('</tbody>').iterate();
var res = [];
var temp = [];
var away_ticker = "";
scraped.forEach(function(e){
var away_team = Parser.data(e).from('href="mlb/team/_/name/').to('"').build();
var time = Parser.data(e).from('a data-dateformat="time1"').to('</a>').build();
if (away_ticker == "") away_ticker = away_team;
if (away_team != away_ticker) {
temp.splice(1, 0, away_ticker);
res.push(temp);
temp = [];
away_ticker = away_team;
temp.push(time);
}
});
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Schedule");
ss.getRange(ss.getLastRow() + 1, 1, res.length, res[0].length).setValues(res);
}
I get the following error:
TypeError: Cannot read property "length" from undefined. (line 42, file "Code")
Here is a modified solution that works
function espn_schedule() {
var url = "http://www.espn.com/mlb/schedule/_/date/20180329";
var content = UrlFetchApp.fetch(url).getContentText();
var e = Parser.data(content).from('class="schedule has-team-logos align-left"').to('</tbody>').build();
var res = [];
//Logger.log(scraped[0])
var temp = [];
var away_ticker = "";
var teams = Parser.data(e).from('<abbr title="').to('">').iterate();
Logger.log(teams)
var time = Parser.data(e).from('data-date="').to('">').iterate()
Logger.log(time)
for( var i = 0; i<teams.length ; i = i+2)
{
res[i/2] = []
res[i/2][0] = teams[i]
res[i/2][1] = teams[i+1]
res[i/2][2] = new Date(time[i/2]).toLocaleTimeString('en-US')
}
Logger.log(res)
var ss = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Schedule");
ss.getRange(ss.getLastRow() + 1, 1, res.length, res[0].length).setValues(res);
}
Modification explained:
1) Since you access only the first table you don't need to iterate during parsing and just get the first table. Also, since you get just the first table, you don't need to use forEach to loop through each element.
var e = Parser.data(content)
.from('class="schedule has-team-logos align-left"')
.to('</tbody>')
.build(); //Use build instead of iterate
2) Instead of parsing the HTML link to get the team name, you can use <abbr title=" element to scrape the name. Furthermore, you can iterate over all the team names in the table to get an array of team names.
var teams = Parser.data(e).from('<abbr title="').to('">').iterate();
3) Similar to the above modification, you can get the time by using the data-date tag. This gives you date which can read by Date() class. Again, we iterate over the table to get all the times
var time = Parser.data(e).from('data-date="').to('">').iterate()
4) Finally, we use for loop to rearrange the teams and time in the array called res. This allows for inserting the data into the sheet directly.
for( var i = 0; i<teams.length ; i = i+2) //each loop adds 2 to the counter
{
res[i/2] = []
res[i/2][0] = teams[i] //even team (starts at zero)
res[i/2][1] = teams[i+1] //vs odd teams
res[i/2][2] = new Date(time[i/2]).toLocaleTimeString('en-US')
}
Reference:
Date(),Date.toLocaleTimeString()
Edit:
Reason for error, in the below code
Parser.data(e).from('href="mlb/team/_/name/').to('"').build()
you are looking for string 'href="mlb/team/_/name/', however it should be href="/mlb/team/_/name/'. Note the difference mlb vs /mlb.
Secondly, in the following code
Parser.data(e).from('a data-dateformat="time1"').to('</a>').build();
The string should be a data-dateFormat, when you inspect the website it shown as dateformat. However, when you call it using URLfetch and log the text, it is shown as dateFormat
I am trying to get the following to work: A JSON is sent to my Google Script - Now I want to get specific values from that JSON Message and store them into specific rows of one specific Google Spreadsheet. So far so good, this is what I have :
function doPost(response) {
var sheets = SpreadsheetApp.openById('MY SHEET ID');
var dataAll = JSON.parse(response.getContentText());
var nR = getNextRow(sheets) + 1;
// RECORD DATA IN SPREADSHEET
sheets.getRangeByName('timestamp').getCell(nR,1).setValue(new Date());
sheets.getRangeByName('ticket_id').getCell(nR,1).setValue(dataAll);
}
function getNextRow(sheets) {
var timestamps = sheets.getRangeByName("timestamp").getValues();
for (i in timestamps) {
if(timestamps[i][0] == "") {
return Number(i);
break;
}}}
It should store the response and put it into a blank cell of the range "timestamp". But nothing happens at this point.
This is the JSON ( Body ) from JIRA:
{"timestamp":1483576902984,"webhookEvent":"jira:issue_created","issue_event_type_name":"issue_created","user":{"self":"https://xxx.atlassian.net/rest/api/2/user?username=admin","name":"admin","key":"admin","emailAddress":"test#mail.at","avatarUrls":{"48x48":"https://secure.gravatar.com/avatar/3d238d8be45bd26982fa09ae2f891c3f?d=mm&s=48","24x24":"https://secure.gravatar.com/avatar/3d238d8be45bd26982fa09ae2f891c3f?d=mm&s=24","16x16":"https://secure.gravatar.com/avatar/3d238d8be45bd26982fa09ae2f891c3f?d=mm&s=16","32x32":"https://secure.gravatar.com/avatar/3d238d8be45bd26982fa09ae2f891c3f?d=mm&s=32"},"displayName":"Max Mustermann [Administrator]","active":true,"timeZone":"Europe/Berlin"},"issue":{"id":"10057","self":"https://xxx.atlassian.net/rest/api/2/issue/10057","key":"TA-58","fields":{"issuetype":{"self":"https://xxx.atlassian.net/rest/api/2/issuetype/10104","id":"10104","description":"A problem which impairs or prevents the functions of the product.","iconUrl":"https://xxx.atlassian.net/secure/viewavatar?size=xsmall&avatarId=10303&avatarType=issuetype","name":"Bug","subtask":false,"avatarId":10303},"timespent":null,"project":{"self":"https://xxx.atlassian.net/rest/api/2/project/10000","id":"10000","key":"TA","name":"Test Area","avatarUrls":{"48x48":"https://xxx.atlassian.net/secure/projectavatar?avatarId=10324","24x24":"https://xxx.atlassian.net/secure/projectavatar?size=small&avatarId=10324","16x16":"https://xxx.atlassian.net/secure/projectavatar?size=xsmall&avatarId=10324","32x32":"https://xxx.atlassian.net/secure/projectavatar?size=medium&avatarId=10324"}},"customfield_10110":null,"fixVersions":[],"customfield_10111":null,"aggregatetimespent":null,"customfield_10112":"Not started","resolution":null,"customfield_10113":null,"customfield_10114":null,"customfield_10104":null,"customfield_10105":null,"customfield_10106":null,"customfield_10107":null,"customfield_10108":null,"customfield_10109":null,"resolutiondate":null,"workratio":-1,"lastViewed":null,"watches":{"self":"https://xxx.atlassian.net/rest/api/2/issue/TA-58/watchers","watchCount":0,"isWatching":false},"created":"2017-01-05T01:41:42.903+0100","priority":{"self":"https://xxx.atlassian.net/rest/api/2/priority/3","iconUrl":"https://xxx.atlassian.net/images/icons/priorities/medium.svg","name":"Medium","id":"3"},"customfield_10100":null,"customfield_10101":null,"customfield_10102":null,"customfield_10103":null,"labels":[],"timeestimate":null,"aggregatetimeoriginalestimate":null,"versions":[],"issuelinks":[],"assignee":null,"updated":"2017-01-05T01:41:42.903+0100","status":{"self":"https://xxx.atlassian.net/rest/api/2/status/10000","description":"","iconUrl":"https://xxx.atlassian.net/","name":"To Do","id":"10000","statusCategory":{"self":"https://xxx.atlassian.net/rest/api/2/statuscategory/2","id":2,"key":"new","colorName":"blue-gray","name":"To Do"}},"components":[],"timeoriginalestimate":null,"description":"super alles neu","timetracking":{},"customfield_10005":null,"attachment":[],"aggregatetimeestimate":null,"summary":"super alles neu","creator":{"self":"https://xxx.atlassian.net/rest/api/2/user?username=admin","name":"admin","key":"admin","emailAddress":"test#mail.at","avatarUrls":{"48x48":"https://secure.gravatar.com/avatar/3d238d8be45bd26982fa09ae2f891c3f?d=mm&s=48","24x24":"https://secure.gravatar.com/avatar/3d238d8be45bd26982fa09ae2f891c3f?d=mm&s=24","16x16":"https://secure.gravatar.com/avatar/3d238d8be45bd26982fa09ae2f891c3f?d=mm&s=16","32x32":"https://secure.gravatar.com/avatar/3d238d8be45bd26982fa09ae2f891c3f?d=mm&s=32"},"displayName":"Max Mustermann [Administrator]","active":true,"timeZone":"Europe/Berlin"},"subtasks":[],"reporter":{"self":"https://xxx.atlassian.net/rest/api/2/user?username=admin","name":"admin","key":"admin","emailAddress":"test#mail.at","avatarUrls":{"48x48":"https://secure.gravatar.com/avatar/3d238d8be45bd26982fa09ae2f891c3f?d=mm&s=48","24x24":"https://secure.gravatar.com/avatar/3d238d8be45bd26982fa09ae2f891c3f?d=mm&s=24","16x16":"https://secure.gravatar.com/avatar/3d238d8be45bd26982fa09ae2f891c3f?d=mm&s=16","32x32":"https://secure.gravatar.com/avatar/3d238d8be45bd26982fa09ae2f891c3f?d=mm&s=32"},"displayName":"Max Mustermann [Administrator]","active":true,"timeZone":"Europe/Berlin"},"customfield_10000":"{}","aggregateprogress":{"progress":0,"total":0},"customfield_10001":null,"customfield_10115":null,"customfield_10116":"0|i0005r:","environment":null,"duedate":null,"progress":{"progress":0,"total":0},"comment":{"comments":[],"maxResults":0,"total":0,"startAt":0},"votes":{"self":"https://xxx.atlassian.net/rest/api/2/issue/TA-58/votes","votes":0,"hasVoted":false},"worklog":{"startAt":0,"maxResults":20,"total":0,"worklogs":[]}}}}
However, I don't want to have the whole JSON in my cell, I only want to have specific obejcts/id from within the JSON. How do I call them ?
After tons of research, this is a solution that works for me (in my case):
function doPost(response) {
var sheets = SpreadsheetApp.openById('SHEET_ID');
// retrieve data from JIRA Payload and store them into "data"
var json = response.postData.contents;
var data = JSON.parse(json);
// index values from "data" and store them into seperate variables
// for example:
var ticket_id = data.issue.key;
var priority_name = data.issue.fields.priority.name;
var summary = data.issue.fields.summary;
This two lines:
var json = response.postData.contents;
var data = JSON.parse(json);
Made it possible to read the body and index all the specific parameters I want.
Here is an example:
/*
* webhookHandler: JIRA webhook callback function
*/
function webhookHandler(response) {
var data = response.getAs("application/json");
//logs out data in dev console
console.log(data);
var spreadsheet = SpreadsheetApp.openById("<spreadsheet id>");
var cellRange = spreadsheet.getRangeByName("<some range name>");
var cell = cellRange.getCell(0 /*row index*/, 0/*column index*/);
cell.setValue(data.ticket_id/*index the JSON object returned by response*/);
}
UrlFetchApp Documentation
SpreadsheetApp Documentation
Using Google App Script, when I used Logger.log() the for loop iterates properly and I get results for each value. When I try to output this to a google sheet only the last value for each variable is output over and over again for the number of goals.length.
Any help is very much appreciated!
function listGoals() {
var sheet = SpreadsheetApp.getActiveSheet();
var filterList = Analytics.Management.Goals.list(accountId, webPropertyId, profileId)
var goals = filterList.items;
for (var i = 0, goal; goal = goals[i]; i++) {
var accountId = goal.accountId;
var propertyId = goal.webPropertyId;
var goalNumber = goal.id;
var goalName = goal.name;
Logger.log('accountId: ' + accountId);
Logger.log('profileId: ' + propertyId);
Logger.log('goal number: ' + goalNumber);
Logger.log('goal name: ' + goalName);
//Logger.log prints for each result
sheet.getRange(1,1,goals.length).setValue(goalNumber);
sheet.getRange(1,2,goals.length).setValue(goalName);
//this only prints out the last value of goalNumber and goalName to the sheet
}
}
It doesn't only print the last results, it just keeps overwriting the old result with the new one.
goals.length only helps if you then supply an array of arrays containing the values looking as such:
[[1, "Goal 1"],
[2, "Goal 2"]]
If you want to print out a list of goalNumber and goalName you need to offset the cell to write in every time.
something like
sheet.getRange(1+i,1).setValue(goalNumber);
sheet.getRange(1+i,2).setValue(goalName);
To speed up the process a bit and not do two calls for every goal you can store the id name pairs as arrays within an array and do one final setValues call after the loop finishes executing.
function listGoals() {
var sheet = SpreadsheetApp.getActiveSheet();
var filterList = Analytics.Management.Goals.list(accountId, webPropertyId, profileId)
var goals = filterList.items;
var goalsToWrite = [];
for (var i = 0, goal; goal = goals[i]; i++) {
goalsToWrite.push([goal.id, goal.name]);
}
sheet.getRange(1, 1, goals.length, 2).setValues(goalsToWrite);
}