I tried coding like spreadsheet API batch copy https://developers.google.com/google-apps/spreadsheets/#updating_multiple_cells_with_a_batch_request, The sample is base on same spreadsheet, I added a target cell but always get same error
com.google.gdata.client.batch.BatchInterruptedException: Batch Interrupted (some operations might have succeeded) : a response has already been sent for batch operation update id='R1C1'
My code like this
SpreadsheetService spreadsheetService = getSpreadsheetService(currentEmail);
WorksheetFeed feed = spreadsheetService.getFeed(getWorksheetFeedURL(sourceId), WorksheetFeed.class);
SpreadsheetEntry targetFeed = spreadsheetService.getEntry(getSpreadsheetFeedURL(targetId), SpreadsheetEntry.class);
SpreadsheetEntry sourceFeed = spreadsheetService.getEntry(getSpreadsheetFeedURL(sourceId), SpreadsheetEntry.class);
for(WorksheetEntry entry:feed.getEntries()){
WorksheetEntry targetWorksheet = spreadsheetService.insert(targetFeed.getWorksheetFeedUrl(), entry);
FeedURLFactory urlFactory = FeedURLFactory.getDefault();
URL cellFeedUrl = urlFactory.getCellFeedUrl(sourceFeed.getKey(), "od6", "private", "full");
URL targetFeedUrl = urlFactory.getCellFeedUrl(targetFeed.getKey(), "od6", "private", "full");
CellFeed cellFeed = spreadsheetService.getFeed(targetFeedUrl, CellFeed.class);
List<CellAddress> cellAddrs = new ArrayList<CellAddress>();
for (int row = 1; row <= entry.getRowCount(); ++row) {
for (int col = 1; col <= entry.getColCount(); ++col) {
cellAddrs.add(new CellAddress(row, col));
}
}
Map<String, CellEntry> cellEntries = getCellEntryMap(spreadsheetService, cellFeedUrl, cellAddrs);
CellFeed batchRequest = new CellFeed();
for (CellAddress cellAddr : cellAddrs) {
URL entryUrl = new URL(targetFeedUrl.toString() + "/" + cellAddr.idString);
CellEntry batchEntry = new CellEntry(cellAddr.row, cellAddr.col, cellAddr.idString);
String inputValue = cellEntries.get(cellAddr.idString).getCell().getInputValue();
batchEntry.changeInputValueLocal(inputValue);
batchEntry.setId(String.format("%s/%s", targetFeedUrl.toString(), cellAddr.idString));
System.out.println(targetFeedUrl.toString()+": "+cellAddr.idString+" "+ inputValue);
BatchUtils.setBatchId(batchEntry, cellAddr.idString);
BatchUtils.setBatchOperationType(batchEntry, BatchOperationType.UPDATE);
batchRequest.getEntries().add(batchEntry);
}
spreadsheetService.setHeader("If-Match", "*");
// Submit the update
Link batchLink = cellFeed.getLink(ILink.Rel.FEED_BATCH, ILink.Type.ATOM);
CellFeed batchResponse = spreadsheetService.batch(new URL(batchLink.getHref()), batchRequest);
boolean isSuccess = true;
for (CellEntry entry1 : batchResponse.getEntries()) {
String batchId = BatchUtils.getBatchId(entry);
if (!BatchUtils.isSuccess(entry1)) {
isSuccess = false;
BatchStatus status = BatchUtils.getBatchStatus(entry);
}
}
spreadsheetService.setHeader("If-Match", null);
Batch copy:
What is the fastest way to update a google spreadsheet with a lot of data through the spreadsheet api?
There is a Bug with cell references such as $A5.
They will not write to the spreadsheet. While both A5 and $A$5 work, references with just one $ in cause a problem. I forget the fine detail.
Related
I have a master spreadsheet with several drivers and their routes for the day. This needs to be parsed out to several driver sheets. I have one sheet with all the data in the master and another with all the drivers names and spreadsheet ids to run a loop through for all drivers listed. Just runs...no action. Last three comments need to be fixed as well as it errors here and does not like .openById.
function setRoutes(){
var ScheduleSheetURL = "https://docs.google.com/spreadsheets/...";
var ActiveSpreadSheet = SpreadsheetApp.openByUrl(ScheduleSheetURL);
var AllRoutesSheetName = "RoutesIn";
var ActiveSheet = ActiveSpreadSheet.setActiveSheet(ActiveSpreadSheet.getSheetByName(AllRoutesSheetName));
var AllRouteData = ActiveSheet.getRange('A:AZ').getValues();
ActiveSheet = SpreadsheetApp.setActiveSheet(ActiveSpreadSheet.getSheetByName("Drivers"));
//var ActiveDriverCounter = ActiveSheet.getRange("A1:A").getValues();
//var DriverCount = ActiveDriverCounter.filter(String).length;
var DriverCount = ActiveSheet.getLastRow()-1;
var idrvSheetID = "";
var ActiveDriverName = "";
var RouteData = [];
var i = 1;
for (i = 1; i++; i <= DriverCount){
//Get Route Name
ActiveSpreadSheet = SpreadsheetApp.openByUrl(ScheduleSheetURL);
ActiveSheet = ActiveSpreadSheet.setActiveSheet(ActiveSpreadSheet.getSheetByName("Drivers"));
ActiveDriverName = ActiveSheet.getRange(i,1).getValue();
Logger.log(ActiveDriverName);
//Get Drivers Route
ActiveSheet = ActiveSpreadSheet.setActiveSheet(ActiveSpreadSheet.getSheetByName(AllRoutesSheetName));
for (var i = 0; i< AllRouteData.length; i++){
if(AllRouteData[i][1] == ActiveDriverName){
RouteData.push(AllRouteData[i])
Logger.log(RouteData[i]);
}
}
ActiveSpreadSheet = SpreadsheetApp.openByUrl(ScheduleSheetURL);
//Open & Write Driver Sheet
ActiveSheet = SpreadsheetApp.setActiveSheet(ActiveSpreadSheet.getSheetByName("Drivers"));
idrvSheetID = ActiveSheet.getRange(i,2).getValue();
// ActiveSpreadSheet = SpreadsheetApp.openById(idrvSheetID);
// ActiveSheet = ActiveSpreadSheet.setActiveSheet(ActiveSpreadSheet.getSheets()[0]);
// ActiveSheet.getRange(ActiveSheet.getLastRow()+1,1,RouteData.length,RouteData[0].length).setValues(RouteData);
}
}
Issues:
There are several problems with your script:
Every time you want to work on a spreadsheet or sheet, you are using some variant of setActive. I guess you are under the assumption that you need to "activate" a sheet in order to work on it, but that's not true! If you remove all this, your code can be minimized considerably.
You are not defining the outer for loop correctly: for (i = 1; i++; i <= DriverCount){. Second parameter should be the condition, and third one should be what to do at the end of each loop iteration. You have it the other way around.
When trying to filter the routes according to the Driver (if(AllRouteData[i][1] == ActiveDriverName){), you are not specifying the correct array index (arrays are 0-indexed, so 1 here would refer to column B, which doesn't contain the driver's names, so the script won't find any match there.
You are reusing the same loop counter variable (i) in a nested loop (for (var i = 0; i< AllRouteData.length; i++){). This will mess with your outer loop.
Solution:
Considering all these, I've wrote a small script that accomplishes your purpose. I have changed some of the variables names so that they are more meaningful (please check inline comments):
function setRoutes() {
const ScheduleSheetURL = "YOUR-MAIN-SPREADSHEET-URL"; // Change to your spreadsheet URL
const SourceSpreadSheet = SpreadsheetApp.openByUrl(ScheduleSheetURL);
const RoutesSheet = SourceSpreadSheet.getSheetByName("RoutesIn");
const AllRouteData = RoutesSheet.getRange('A:AZ').getValues();
const DriversSheet = SourceSpreadSheet.getSheetByName("Drivers");
const DriverCount = DriversSheet.getLastRow()-1;
const DriversData = DriversSheet.getRange(2, 1, DriverCount, 2).getValues(); // Get all Drivers data
for (let i = 0; i < DriversData.length; i++) { // Loop through all Drivers data
const DriverData = DriversData[i]; // Specific Driver data
const DriverName = DriverData[0]; // Driver name (column A)
const DriverSpreadsheetId = DriverData[1]; // Driver spreadsheet ID (column B)
const DriverRoutes = AllRouteData.filter(routeData => routeData[0] === DriverName); //Filter routes according to driver
const DriverSpreadsheet = SpreadsheetApp.openById(DriverSpreadsheetId); // Get specific Driver spreadsheet
const DriverSheet = DriverSpreadsheet.getSheets()[0]; // Get first sheet in Driver spreadsheet
DriverSheet.getRange(DriverSheet.getLastRow() + 1, 1, DriverRoutes.length, DriverRoutes[0].length)
.setValues(DriverRoutes); // Add routes to driver spreadsheet
}
}
I have a sheet where hyperlink is set in cell, but not through formula. When clicked on the cell, in "fx" bar it only shows the value.
I searched on web but everywhere, the info is to extract hyperlink by using getFormula().
But in my case there is no formula set at all.
I can see hyperlink as you can see in image, but it's not there in "formula/fx" bar.
How to get hyperlink of that cell using Apps Script or any formula?
When a cell has only one URL, you can retrieve the URL from the cell using the following simple script.
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Sheet1");
var url = sheet.getRange("A2").getRichTextValue().getLinkUrl(); //removed empty parentheses after getRange in line 2
Source: https://gist.github.com/tanaikech/d39b4b5ccc5a1d50f5b8b75febd807a6
When Excel file including the cells with the hyperlinks is converted to Google Spreadsheet, such situation can be also seen. In my case, I retrieve the URLs using Sheets API. A sample script is as follows. I think that there might be several solutions. So please think of this as one of them.
When you use this script, please enable Sheets API at Advanced Google Services and API console. You can see about how to enable Sheets API at here.
Sample script:
var spreadsheetId = "### spreadsheetId ###";
var res = Sheets.Spreadsheets.get(spreadsheetId, {ranges: "Sheet1!A1:A10", fields: "sheets/data/rowData/values/hyperlink"});
var sheets = res.sheets;
for (var i = 0; i < sheets.length; i++) {
var data = sheets[i].data;
for (var j = 0; j < data.length; j++) {
var rowData = data[j].rowData;
for (var k = 0; k < rowData.length; k++) {
var values = rowData[k].values;
for (var l = 0; l < values.length; l++) {
Logger.log(values[l].hyperlink) // You can see the URL here.
}
}
}
}
Note:
Please set spreadsheetId.
Sheet1!A1:A10 is a sample. Please set the range for your situation.
In this case, each element of rowData is corresponding to the index of row. Each element of values is corresponding to the index of column.
References:
Method: spreadsheets.get
If this was not what you want, please tell me. I would like to modify it.
Hey all,
I hope this helps you save some dev time, as it was a rather slippery one to pin down...
This custom function will take all hyperlinks in a Google Sheets cell, and return them as text formatted based on the second parameter as either [JSON|HTML|NAMES_ONLY|URLS_ONLY].
Parameters:
cellRef : You must provide an A1 style cell reference to a cell.
Hint: To do this within a cell without hard-coding
a string reference, you can use the CELL function.
eg: "=linksToTEXT(CELL("address",C3))"
style : Defines the formatting of the output string.
Valid arguments are : [JSON|HTML|NAMES_ONLY|URLS_ONLY].
Sample Script
/**
* Custom Google Sheet Function to convert rich-text
* links into Readable links.
* Author: Isaac Dart ; 2022-01-25
*
* Params
* cellRef : You must provide an A1 style cell reference to a cell.
* Hint: To do this within a cell without hard-coding
* a string reference, you can use the CELL function.
* eg: "=linksToTEXT(CELL("address",C3))"
*
* style : Defines the formatting of the output string.
* Valid arguments are : [JSON|HTML|NAMES_ONLY|URLS_ONLY].
*
*/
function convertCellLinks(cellRef = "H2", style = "JSON") {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var cell = sheet.getRange(cellRef).getCell(1,1);
var runs = cell.getRichTextValue().getRuns();
var ret = "";
var lf = String.fromCharCode(10);
runs.map(r => {
var _url = r.getLinkUrl();
var _text = r.getText();
if (_url !== null && _text !== null) {
_url = _url.trim(); _text = _text.trim();
if (_url.length > 0 && _text.length > 0) {
switch(style.toUpperCase()) {
case "HTML": ret += '' + _text + '}' + lf; break;
case "TEXT": ret += _text + ' : "' + _url + '"' + lf; break;
case "NAMES_ONLY" : ret += _text + lf; break;
case "URLS_ONLY" : ret += _url + lf; break;
//JSON default : ...
default: ret += (ret.length>0?(','+ lf): '') +'{name : "' + _text + '", url : "' + _url + '"}' ; break;
}
ret += lf;
}
}
});
if (style.toUpperCase() == "JSON") ret = '[' + ret + ']';
//Logger.log(ret);
return ret;
}
Cheers,
Isaac
I tried solution 2:
var urls = sheet.getRange('A1:A10').getRichTextValues().map( r => r[0].getLinkUrl() ) ;
I got some links, but most of them yielded null.
I made a shorter version of solution 1, which yielded all the links.
const id = SpreadsheetApp.getActive().getId() ;
let res = Sheets.Spreadsheets.get(id,
{ranges: "Sheet1!A1:A10", fields: "sheets/data/rowData/values/hyperlink"});
var urls = res.sheets[0].data[0].rowData.map(r => r.values[0].hyperlink) ;
I am working on a project which is an E-health patient Gateway.
I want to generate a number (int type) against a submit form from View to Controller (When phlebotomist receives the blood sample he will check the box and click on submit button). This number is the patient's blood sample number that will be generated in my controller and will be saved in the database. I am using the logic below to create the number but it always gives me zero.
Kindly let me know how I can create numbers one by one in sequence and store in database?
public ActionResult AddSampleTest(int Pid, int Tid, int Stid, string Comment ,string testDate,int DotorID)
{
int sampleNumber=1;
Random rnd = new Random();
int[] sample = new int[50000];
rnd.Next();
for (int ctr = 1; ctr <= sample.Length; ctr++)
{
sampleNumber=sample[ctr + 1];
}
string sampleno = sampleNumber.ToString();
DateTime TestDate = Convert.ToDateTime(testDate);
int Recomended_Test_DoctorID = DotorID;
// Update the Sample Status
PatientTest objpatientTest = db.PatientTests.Where(x => x.PatientID == Pid && x.Testid == Tid && x.SubTestId == Stid && x.Date==TestDate &&x.DoctorId== Recomended_Test_DoctorID).FirstOrDefault();
objpatientTest.SampleStatus = "Sample Received";
objpatientTest.SampleNumber = sampleno;
db.SaveChanges();
var name = User.Identity.Name;
int Lid = db.LabAttendantRecords.Where(x => x.Name == name).Select(x => x.User_id).FirstOrDefault();
LabTestSample obj = new LabTestSample();
obj.labtestid = Tid;
obj.PatientId = Pid;
obj.subtestid = Stid;
obj.labAtendid = Lid;
obj.date = DateTime.Now.Date;
obj.sampleReceived = true;
obj.Comment = sampleno;
db.LabTestSamples.Add(obj);
db.SaveChanges();
return RedirectToAction("TestSampleNumber", "LabAttendantDashboard", new { sampleno});
// Use This for add Result
return RedirectToAction("SubTest", "LabAttendantDashboard", new { Pid, Tid, Stid });
}
sampleNumber is always 0 because each element in sample is 0.
If you want it to be a random number you need to do something like :
Random rnd = new Random();
int sampleNumber = rnd.Next();
string sampleno = sampleNumber.ToString();
If you need to be sure that the sample number is unique, you should use a Guid :
string sampleno = Guid.NewGuid().ToString();
public ActionResult PartTimeFacultyCourseLoadReport()
{
var teacherStatistics = (from t in db.Teachers
join c in db.Courses
on t.Id equals c.TeacherId into cGroup
where t.Status == "Part Time"
orderby t.Designation descending
select new
{
TeacherInfo = t,
CourseInfo = from cg in cGroup
orderby cg.Code ascending
select cg
}).ToList();
List<TeacherStatistics> teacherStatisticses = new List<TeacherStatistics>();
int count = 0;
foreach (var teacherStatistic in teacherStatistics)
{
TeacherStatistics aTeacherStatistics = new TeacherStatistics();
aTeacherStatistics.Name = teacherStatistic.TeacherInfo.Name;
aTeacherStatistics.Designation = teacherStatistic.TeacherInfo.Designation;
aTeacherStatistics.NumberOfCourse = teacherStatistic.TeacherInfo.NumberOfCourse;
count = 0;
foreach (var courseInfo in teacherStatistic.CourseInfo)
{
if (count != 0)
{
aTeacherStatistics.Courses += ", ";
}
aTeacherStatistics.Courses += courseInfo.Code;
aTeacherStatistics.Courses += "(";
aTeacherStatistics.Courses += courseInfo.Section;
aTeacherStatistics.Courses += ")";
count++;
}
teacherStatisticses.Add(aTeacherStatistics);
}
var document = new Document(PageSize.A4, 50, 50, 25, 25);
var output = new MemoryStream();
var writer = PdfWriter.GetInstance(document, output);
document.Open();
var data = teacherStatisticses.ToList();
document.Add(data);
Response.ContentType = "application/pdf";
Response.AddHeader("content-disposition", "attachment; filename=PartTimeFaculty.pdf");
Response.BinaryWrite(output.ToArray());
document.Close();
return View(teacherStatisticses);
}
I want to pass a list named 'teacherStatisticses' through document object for making a PDF. My code doesn't work. It showed me following Error -
Argument 1: cannot convert from 'System.Collections.Generic.List' to 'iTextSharp.text.IElement'
I assume that you are using some version of itextpdf for the PDF generation.
The error is in the line:
document.Add(data);
There is no way to add plain .NET objects into the PDF document. I can not predict the structure of PDF which you`d like to achieve, but the code mentioned above could be written as:
foreach(var teacher in teacherStatistics)
{
var paragraph = new Paragraph(teacher.ToString()); // instead of teacher.ToString() should be some code which translates teacherStatistics projection to the string representation
document.Add(paragraph);
}
//not tested
A lot of useful samples could be found at
http://developers.itextpdf.com/examples
Basic tutorial for the itextpdf:
http://developers.itextpdf.com/content/itext-7-jump-start-tutorial/chapter-1-introducing-basic-building-blocks
When working with google spreadsheet, how to download all the sheets at once?
I want to use the option:
Comma-separated values
But it only download the current sheet, how to get them all?
For anyone who navigates to this question, trying to download all the tabs in their Google spreadsheets as CSV files at once, even in 2021, there does not seem to be a GUI button to do this. At least I could not see anything. The answer by #Amit Agarwal does well, to get all sheets, but if your file has comma-delimited data in cells, then data could get mangled.
I took Amit's approach https://stackoverflow.com/a/28711961 and combined it with Michael Derazon and Aaron Davis's approach here https://gist.github.com/mrkrndvs/a2c8ff518b16e9188338cb809e06ccf1 to dump all the tabs of a chosen Google spreadsheet into a folder in Google Drive. You can then just download the folder with a single click.
The following is Google script, not exactly a Javascript, and you would have to copy-paste this in https://script.google.com/ login with your Google id, and then create a project and then create a script app, and save and execute this.
// https://stackoverflow.com/a/28711961
function export_sheets_as_csv_to_folder() {
// Sheet id is in URL https://docs.google.com/spreadsheets/d/YOUR_SHEET_ID/edit#gid=IGNORE
var ss = SpreadsheetApp.openById('YOUR_SHEET_ID');
var sheets = ss.getSheets();
if (sheets === undefined || sheets.length === 0) {
return;
}
var passThroughFolder = DriveApp.createFolder('YOUR_PREFERRED_FOLDER_NAME_IN_DRIVE');
for (var s in sheets) {
var csv = convertRangeToCsvFile_(sheets[s])
passThroughFolder.createFile(sheets[s].getName() + ".csv", csv);
}
}
// https://gist.github.com/mrkrndvs/a2c8ff518b16e9188338cb809e06ccf1
function convertRangeToCsvFile_(sheet) {
// get available data range in the spreadsheet
var activeRange = sheet.getDataRange();
try {
var data = activeRange.getValues();
var csvFile = undefined;
// loop through the data in the range and build a string with the csv data
if (data.length > 1) {
var csv = "";
for (var row = 0; row < data.length; row++) {
for (var col = 0; col < data[row].length; col++) {
if (data[row][col].toString().indexOf(",") != -1) {
data[row][col] = "\"" + data[row][col] + "\"";
}
}
// join each row's columns
// add a carriage return to end of each row, except for the last one
if (row < data.length-1) {
csv += data[row].join(",") + "\r\n";
}
else {
csv += data[row];
}
}
csvFile = csv;
}
return csvFile;
}
catch(err) {
Logger.log(err);
Browser.msgBox(err);
}
}
After clicking download > pdf, select export > worksheet (instead of current sheet which is the default)
You can use Google Scripts to save all the sheets of a spreadsheet into separate files.
function myFunction() {
var ss = SpreadsheetApp.openById(SHEET_ID);
var sheets = ss.getSheets();
for (var s in sheets) {
var csv = "";
var data = sheets[s].getDataRange().getValues();
for (d in data) {
csv += data[d].join(",") + "\n";
}
DriveApp.createFile(sheets[s].getName() + ".csv", csv);
}
}
the answer from #Soham works amazingly but it doesn't handle multiline values. It would be an easy fix just to add more checks to character \n along with , but I took the liberty to rewrite the function using map (and string.includes) so it is more concise.
function convertRangeToCsvFile_(sheet) {
return sheet.getDataRange().getValues()
.map(row => row.map(value => value.toString())
.map(value => (value.includes("\n") || value.includes(",")) ? "\"" + value + "\"" : value)
.join(','))
.join('\n')
}
A slight variation on this that uses a zip instead of a folder to contain the sheets and does some modernizing of the great work done by keychera and Soham's answer.
You can use this as a bound script and it will add a menu item to the extensions menu:
// Code.gs
function exportSheetsToDrive() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheets = ss.getSheets();
if (sheets === undefined || sheets.length === 0) {
return;
}
const now = new Date();
const csvBlobs = sheets.map((sheet) => {
const name = sheet.getName();
const csv = convertSheetToCsv(sheet);
Logger.log({ name, length: csv.length });
return Utilities.newBlob(csv, MimeType.CSV, `${name}.csv`)
});
const zipName = `export_${ss.getName()}_${now.toISOString()}.zip`;
const zip = Utilities.zip(csvBlobs, zipName);
DriveApp.createFile(zip);
}
function convertSheetToCsv(sheet) {
return sheet
.getDataRange()
.getValues()
.map((row) =>
row
.map((value) => value.toString())
.map((value) =>
value.includes("\n") || value.includes(",")
? '"' + value + '"'
: value
)
.join(",")
)
.join("\n");
}
and
// Menu.gs
function onOpen(e) {
const menu = SpreadsheetApp.getUi().createAddonMenu();
menu
.addItem('Export all sheets as CSV to Drive', 'exportSheetsToDrive')
.addToUi();
}
function onInstall(e) {
onOpen(e);
}