Export Google Sheet as TXT file (Fixed Width) - google-apps-script

I am trying to replicate some functionality from a Excel document with some macros in a Google Spreadsheet. The end goal is to export a fixed-width txt file. Unfortunately, the vendor cannot use a CSV file. So, does anybody know of a way to generate a fixed-width TXT file using Google Scripts?

Just in case anybody else comes looking. I used a few sources and came up with this:
function saveAsFixedWidthTxt() {
// get Spreadsheet Name
var fileName = SpreadsheetApp.getActiveSpreadsheet().getName();
//var fileName = SpreadsheetApp.getActiveSheet().getSheetName();
// get Directory the Spreadsheet is stored in.
var dirName = DocsList.getFileById(SpreadsheetApp.getActive().getId()).getParents()[0].getName();
// Add the ".txt" extension to the file name
fileName = fileName + ".txt";
// Convert the range data to fixed width format
var txtFile = convertRangeToTxtFile_(fileName);
// Delete existing file
deleteDocByName(fileName);
// Create a file in the Docs List with the given name and the data
DocsList.getFolder(dirName).createFile(fileName, txtFile);
}
function convertRangeToTxtFile_(txtFileName) {
try {
var txtFile = undefined;
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var rows = sheet.getDataRange();
var data = rows.getValues();
// Loop through the data in the range and build a string with the data
if (data.length > 1) {
var txt = "";
for (var row = 2; row < data.length; row++) {
for (var j=6; j<=10; j++){
if(data[row][j] != ''){
// Employee ID
var empID = "" + data[row][3];
txt += empID;
//Fill to 6 characters
for (i=empID.length; i<6; i++){
txt += " ";
}
// Name
var fullName = data[row][5] + " " + data[row][4]
txt += fullName;
//Fill to 43 characters
for (i=fullName.length; i<44; i++){
txt += " ";
}
// etc, etc to build out your line
txt += "\r\n";
}
}
}
txtFile = txt;
}
return txtFile;
}
catch(err) {
Logger.log(err);
Browser.msgBox(err);
}
}
function deleteDocByName(fileName){
var docs=DocsList.find(fileName)
for(n=0;n<docs.length;++n){
if(docs[n].getName() == fileName){
var ID = docs[n].getId()
DocsList.getFileById(ID).setTrashed(true)
}
}
}

This will work:
export the sheet as a xlsx, then open that file in Excel.
Export that file as Space Delimited Text (.prn, for some reason).
Change the file extension on the resultant file to .txt.
This will result in a file like this:
Column Another Column 3
Val 1 2 $ 5.00
Or do you need to get a .txt file directly out of Google Apps Script?
Edit since a direct .txt download is necessary.
Here's how you could design a script to do that:
Find a way to convert sheet.getColumnWidth(n) to a number of spaces. You may find that there are 5 pixels to 1 character, or whatever the ratio
Run getColumnWidth() for each column to find the width you need for each column. Alternatively, find the length longest string in each cell.
Go through each cell and add it to a large string you begin building. As you add it, add the number of spaces necessary to match the value converted from getColumnWidth(). At the end of each row, append \n to represent a new line.
Once the output string is complete, you could then use the Content Service to download the text like so:
ContentService.createTextOutput(youBigString).downloadAsFile('filename.txt')
This would involve deploying your script as a web app, which could work well - you'd go to a URL and the page would trigger a download of the fixed-width file.

In my case, DocList use returns ReferenceError.#MSCF your lines 7 & 27 :
var dirName = DocsList.getFileById(SpreadsheetApp.getActive().getId()).getParents()[0].getName();
DocsList.getFolder(dirName).createFile(fileName, txtFile);
become
var dirName = DriveApp.getFileById(SpreadsheetApp.getActiveSpreadsheet().getId()).getParents().next().getId();
DriveApp.getFileById(SpreadsheetApp.getActiveSpreadsheet().getId()).getParents().next().createFile(fileName, txtFile);

Related

Exporting big google sheets file as multiple csv with batch size 500

I would like to download a 10000 row long singular sheet from Google Sheets split into 20 separate csv files with batch size of 500 rows each. Could this be possibly done using Google Sheets?
I wasn't able to find a native support for sheets to download and separate the files into multiple parts/fragments. But I was able to find a Google Apps Script code that saves the sheet into a csv.
So what I did was modify the code and then make it turn a sheet into multiple csv's.
The catch is, it will upload all those files into a folder in the logged in google drive and then you can download them in a zip file. (I'll show this later below)
Here is the source code.
And here is the modified code I did:
function saveAsCSV() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
// Update numFiles into how many files you want is outputted
// The script will calculate how many rows it can place in a file equally
// Excess will be placed into an additional file
var numFiles = 5;
var rowsPerFile = Math.floor(sheet.getDataRange().getLastRow() / numFiles);
// create a folder from the name of the spreadsheet
var folder = DriveApp.createFolder(ss.getName().toLowerCase().replace(/ /g,'_') + '_csv_' + new Date().getTime());
// append ".csv" extension to the sheet name
fileName = sheet.getName();
// convert all available sheet data to csv format
var csvFiles = convertRangeToCsvFile_(sheet, rowsPerFile);
// create a file in the Docs List with the given name and the csv data
csvFiles.forEach(function(csvFile, i){
var csvName = fileName + '_' + (i + 1) + '.csv';
folder.createFile(csvName, csvFile);
});
}
function convertRangeToCsvFile_(sheet, rowsPerFile) {
// get available data range in the spreadsheet
var activeRange = sheet.getDataRange();
try {
var data = activeRange.getValues();
var csvFiles = [];
// 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];
}
if ((row + 1) % rowsPerFile === 0) {
csvFiles.push(csv);
csv = "";
}
}
}
// If there are remainders that doesnt reach the number of files needed.
// It will add another file, containing all remainder rows there.
// 10010 rows / 20 files = 10 rows excess
// 21st file will contain those 10 rows excess
// No excess means no 21st file
if(csv) {
csvFiles.push(csv);
}
return csvFiles;
}
catch(err) {
Logger.log(err);
}
}
Sample Data:
Sample Output:
Upon right clicking the folder created in Google Drive, you will have the option to download the whole folder as zip file containing all the csv files.
Here is the downloaded zip file containing all csv files:
Since there are 22 rows and the sample code wants 5 files, a 6th file was created containing the excess 2 rows

Google Sheets Logger values into rows

I am pulling data from a .csv file generated after a URL request using UrlFetchApp.fetch and storing the data using the logger.
Is there a way to write each row of fetched data from the logger into separate rows in a spreadsheet?
I did this quite a while ago. It's how I import data from website visitor logs. To review them. I don't use commas because you find them in the data too often. I use 3 tildes '~~~' instead and the lines are separated with a line feed '\n'.
But I basically split the lines and then fields on each line into array and feed them into the sheets one line at a time and usually I deal with 20 or 30 files about 100K or less. And I find that it loads the files very quickly.
function importData1(myFolderID,myFolderName,myFileName) {
var myFolderID = typeof(myFolderID) !== 'undefined' ? myFolderID : 'FolderID';
var myFileName = typeof(myFileName) !== 'undefined' ? myFileName : '';
if(myFileName && myFolderID)
{
var fi = DriveApp.getFolderById(myFolderID).getFilesByName(myFileName); // Selected IPLogYYMMDD.txt file
var ssid = SpreadsheetApp.getActive().getId();
var ss = SpreadsheetApp.openById(ssid);
if (fi.hasNext() ) // proceed if file exists in the IPlogs folder
{
var file = fi.next();
var data = file.getBlob().getDataAsString();
var lines = data.split('\n');
var newsheet = ss.insertSheet(myFolderName + '/' + myFileName);
var j=0;
for (var i=0; i<lines.length; i++ )
{
var fields = lines[i].split('~~~');
if(fields.length>=8)//There's supposed to be 8 or 9 fields
{
Logger.log('i=' + i + 'fields.length=' + fields.length);
newsheet.getRange(j+1, 1, 1,fields.length).setValues([fields]);
j=j+1;
}
}
}
}
else
{
displayStatus('Error Importing Data','Either Folder or File not found in importData1');
}
Most of the variables are easy to figure out. You'll probably be able to adapt it to what you need. And probably there will be several optional answers for you to chose from.

Dynamic Hyperlink based on cell contents in google sheets?

There is a list of items in a google spreadsheet, that we need to link a bunch of files from another folder on the google drive. The link is constructed with the function shown below:
function bscott () {
var ss=SpreadsheetApp.getActiveSpreadsheet();
var s=ss.getActiveSheet();
var c=s.getActiveCell();
var fldr=DriveApp.getFolderById("**FOLDER ID - not included for security**");
var files=fldr.getFiles();
var names=[],f,str;
while (files.hasNext()) {
f=files.next();
str='=hyperlink("' + f.getUrl() + '","' + f.getName() + '")';
names.push([str]);
}
s.getRange(c.getRow(),c.getColumn(),names.length).setFormulas(names);
}
The problem we have encountered is if the contents of the folder do not match the ordering of our list exactly, the ordering of files to line items gets out of sync. We want certain files to hyperlink to certain cells in our sheet if the contents of another cell in the same row match the contents in the file name. We would like to avoid manually creating links for each line item. Is this possible in google sheets?
Create an object of file names to links:
var objNamesToLinks = {};//Create an empty object to be populated with data
Use your while loop to populate the object:
var fileName = "";//declare variable outside of the loop.
while (files.hasNext()) {
f=files.next();
fileName = f.getName();
str='=hyperlink("' + f.getUrl() + '","' + fileName + '")';
names.push([str]);
objNamesToLinks[fileName] = str;//The object key is the file name
};
The result of the object will be:
objNamesToLinks = {
'fileName1':'Link1',
'fileName2':'Link2',
'fileName3':'Link3'
};
Then get the column of data in your spreadsheet with the file names:
var columnWithFileNames = s.getRange(start Row, column with names, s.getLastRow())
.getValues();
Then loop through the array of data in the column and match the file names up. If there is a match, look up the correct link, and put it into the cell.
var thisFileName, thisMatchedLink;
for (var i=0;i<columnWithFileNames.length;i+=1) {
thisFileName = columnWithFileNames[i][0].trim();
thisMatchedLink = objNamesToLinks[thisFileName];
if (thisMatchedLink !== undefined) {
s.getRange(i,columnToPutLinkInto).setValue(thisMatchedLink);
};
};

How To Download / Export Sheets In Spreadheet Via Google Apps Script

The task is to automate the manual process accomplished by the menu option "File | Download As | Plain Text"
I want to be able to control the saved file name, which cannot be done via the menu.
At the time this is invoked, the user would be sitting on the sheet in the spreadsheet. Ultimately, I'd make it a menu option, but for testing I'm just creating a function that I can run manually.
After reading several other threads for possible techniques, this is what I've come up with.
It builds a custom name for the file, makes the call, and the response code is 200.
Ideally, I'd like to avoid the open / save dialog. In other words, just save the file without additional user intervention. I'd want to save in a specific folder and I've tried it with a complete file spec, but the result is the same.
If I copy the URL displayed in the Logger and paste it into a browser, it initiates the open / save dialog, so that string works.
Here's the code as a function.
function testExportSheet() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var oSheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
var sId = ss.getId();
var ssID=sId + "&gid=" + oSheet.getSheetId();
var url = "https://spreadsheets.google.com/feeds/download/spreadsheets/Export?key="
+ ssID + "&exportFormat=tsv";
Logger.log(url);
var fn = ss.getName() + "-" + oSheet.getSheetName() + ".csv";
var sHeaders = {"Content-Disposition" : "attachment; filename=\"" + fn + "\""};
var sOptions = {"contentType" : "text/html", "headers" : sHeaders};
Logger.log(sOptions);
x = UrlFetchApp.fetch(url, sOptions)
Logger.log(x.getResponseCode());
}
I have exported a spreadsheet as CSV directly into a local hard drive as follows:
Get the CSV content from current sheet using a variation of function convertRangeToCsvFile_() from the tutorial on this page https://developers.google.com/apps-script/articles/docslist_tutorial#section3
var csvFile = convertRangeToCsvFile_(...);
Then select a drive folder that is syncing to a local computer using Drive
var localFolder = DocsList.getFolderById("055G...GM");
And finally save the CSV file into the "local" folder
localFolder.createFile("sample.csv", csvFile);
That's it.
This app script returns a file for download instead of web page to display:
function doGet(){
var outputDocument = DocumentApp.create('My custom csv file name');
var content = getCsv();
var textContent = ContentService.createTextOutput(content);
textContent.setMimeType(ContentService.MimeType.CSV);
textContent.downloadAsFile("4NocniMaraton.csv");
return textContent;
}
In case you are looking to export all of the sheets in s spreadsheet to csv without having to manually do it one by one, here's another thread about it:
Using the google drive API to download a spreadsheet in csv format
The download can be done. But not the "Write to the hard drive" of the computer.
Write issue:
You mean write a file to the hard drive of the computer, using Google Apps Script? Sorry, but you will need more than GAS to do this. For security reasons, I doubt this is possible with only GAS, have never seen anything like this in GAS.
Google Drive API will let you do a download, just needs OAuth and the URL you gave.
This is the function that I found when looking up the same question. This function was linked to by #Mogsdad and the link no longer exists.
function convertRangeToCsvFile_(csvFileName) {
// Get the selected range in the spreadsheet
var ws = SpreadsheetApp.getActiveSpreadsheet().getActiveSelection();
try {
var data = ws.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);
}
}
I found it here and here

Can I generate a file from Google Sheets script?

I'm using Google Sheets to prototype a bunch of numerical data for something I'm doing.
Is there a way to export a subset to a text file?
Effectively, what I'm aiming to do is export a file I can include directly in the build for another project.
So is there a way to generate a text file for download?
If you have a Google Apps account, then you can use DocsList.createFile() to create the text file and save it in your documents list.
Section 3 of this tutorial shows how to save the selected range of a spreadsheet as a file in your documents list in CSV format. It could be modified pretty easily to save in a different format.
I have the texts of my project in some columns of a Google Spreadsheet.
I took this script tutorial from Google and modified it to select only a specific range (in the example below it's D4:D).
It generates a CSV file in your Drive root folder. It still doesn't download the file - I'm working on that now.
Hope it helps!
/* The code below is a modification from this tutorial: https://developers.google.com/apps-script/articles/docslist_tutorial#section3 */
/* The code below is a modification from this tutorial: https://developers.google.com/apps-script/articles/docslist_tutorial#section3 */
function onOpen() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var csvMenuEntries = [{name: "Save as CSV file", functionName: "saveAsCSV"}];
ss.addMenu("CSV", csvMenuEntries);
}
function saveAsCSV() {
// Name the file
fileName = "quests.csv";
// Convert the range data to CSV format
var csvFile = convertRangeToCsvFile_(fileName);
// Create a file in the root of my Drive with the given name and the CSV data
DriveApp.createFile(fileName, csvFile);
}
function convertRangeToCsvFile_(csvFileName) {
// Get from the spreadsheet the range to be exported
var rangeToExport = SpreadsheetApp.getActiveSpreadsheet().getRange("D4:D");
try {
var dataToExport = rangeToExport.getValues();
var csvFile = undefined;
// Loop through the data in the range and build a string with the CSV data
if (dataToExport.length > 1) {
var csv = "";
for (var row = 0; row < dataToExport.length; row++) {
for (var col = 0; col < dataToExport[row].length; col++) {
if (dataToExport[row][col].toString().indexOf(",") != -1) {
//dataToExport[row][col] = "\"" + dataToExport[row][col] + "\"";
dataToExport[row][col] = dataToExport[row][col];
}
}
// Join each row's columns
// Add a carriage return to end of each row, except for the last one
if (row < dataToExport.length-1) {
csv += dataToExport[row].join(",") + "\r\n";
}
else {
csv += dataToExport[row];
}
}
csvFile = csv;
}
return csvFile;
}
catch(err) {
Logger.log(err);
Browser.msgBox(err);
}
}