Google Apps Script creates sheets version of excel file. Issue with multiple creation of versions. - excel

I found a solution for my original question in another post Google Apps Script creates sheets version of excel file.
Testing with the code provided in the answer I ran into another issue. Every time I run the script it creates the Spreadsheets version of the .xlsx files again even if they already exist. I have tried modifying the code withing the last If with no results. Then went back to run your code as posted in case I have missed something but it keeps creating versions of the same files.
Any idea of what could I do to fix this will be really appreciated.
The code provided int he answer is the following.
// Convert the user's stored excel files to google spreadsheets based on the specified directories.
// There are quota limits on the maximum conversions per day: consumer #gmail = 250.
function convertExcelToGoogleSheets()
{
var user = Session.getActiveUser(); // Used for ownership testing.
var origin = DriveApp.getFolderById("origin folder id");
var dest = DriveApp.getFolderById("destination folder id");
// Index the filenames of owned Google Sheets files as object keys (which are hashed).
// This avoids needing to search and do multiple string comparisons.
// It takes around 100-200 ms per iteration to advance the iterator, check if the file
// should be cached, and insert the key-value pair. Depending on the magnitude of
// the task, this may need to be done separately, and loaded from a storage device instead.
// Note that there are quota limits on queries per second - 1000 per 100 sec:
// If the sequence is too large and the loop too fast, Utilities.sleep() usage will be needed.
var gsi = dest.getFilesByType(MimeType.GOOGLE_SHEETS), gsNames = {};
while (gsi.hasNext())
{
var file = gsi.next();
if(file.getOwner().getEmail() == user.getEmail())
gsNames[file.getName()] = true;
}
// Find and convert any unconverted .xls, .xlsx files in the given directories.
var exceltypes = [MimeType.MICROSOFT_EXCEL, MimeType.MICROSOFT_EXCEL_LEGACY];
for(var mt = 0; mt < exceltypes.length; ++mt)
{
var efi = origin.getFilesByType(exceltypes[mt]);
while (efi.hasNext())
{
var file = efi.next();
// Perform conversions only for owned files that don't have owned gs equivalents.
// If an excel file does not have gs file with the same name, gsNames[ ... ] will be undefined, and !undefined -> true
// If an excel file does have a gs file with the same name, gsNames[ ... ] will be true, and !true -> false
if(file.getOwner().getEmail() == user.getEmail() && !gsNames[file.getName()])
{
Drive.Files.insert(
{title: file.getName(), parents: [{"id": dest.getId()}]},
file.getBlob(),
{convert: true}
);
// Do not convert any more spreadsheets with this same name.
gsNames[file.getName()] = true;
}
}
}
}

You want to convert Excel files in origin folder to Google Spreadsheet and put the converted Spreadsheet to dest folder.
When the filename of converted file is existing in dest folder, you don't want to convert it.
If my understanding is correct, how about this modification?
From:
if(file.getOwner().getEmail() == user.getEmail() && !gsNames[file.getName()])
To:
if(file.getOwner().getEmail() == user.getEmail() && !gsNames[file.getName().split(".")[0]])
Note:
In this modification, when the filename of converted file is found in the dest folder, the file is not converted.
When the filename has the extension like ###.xlsx and it is converted to Google Spreadsheet, it seems that the extension is automatically removed. I think that this is the reason that the duplicated files are created. So I used split(".")[0] for this situation.
Reference:
split()

Related

NodeJS - Create a new numbered subdirectory based on the last element of an array based subdir list (Closed)

I have this function that reads a specified working directory using fs.readdir, which then filters out all files and returns an array based list of the first set of subdirectories within the specified working directory, which then grabs the last element (in my case the last subfolder) from the bottom of the array based list.
This list of subdirectories is always returned in order, it's never jumbled.
The way I'm achieving this is with the following code:
var workDir = 'just/some/folder/location';
// Read the specified working directory
fs.readdir(workDir, { withFileTypes: true }, (error, files) => {
if (error) {
//Throw an error if the folder can't be read
console.log('Unable to read the Working Directory! \n\n')
//Print the reason for the error
console.log('REASON: ' + error);
return;
} else {
// Filter out files and return only subdirectories
var subdirList = files
.filter((item) => item.isDirectory())
.map((item) => item.name);
// Get the last subdirectory from the returned list of subdirectories
var lastFolder = subdirList[subdirList.length -1];
// Function to create a new subdir
// Based on the last element/folder
// Returned by the function, needs to go here
// But I can't figure it out
};
});
What I want to be able to do now, is create a new numbered subdirectory within the specified working directory based on the output of the function above.
So for example if the function above ends up detecting folder4 as the last subdirectory, how would I allow it to create folder5? or if the function detects folder5 as the last subdirectory, then how do we allow it to create folder6 and so on??
It's important to note that in my case, the subdirectories inside of my working Directories will always follow a number pattern of 2 through 1000.
Any help on this would be greatly appreciated.
EDIT : You just have to play a bit with the regex pattern, to match your exact requirement. I updated the regex to follow your convetion folder_classes[number].
/[a-zA-Z]+|[0-9]+/g -> /[a-zA-Z_]+|[0-9]+/g
To know how to name your new folder from the previous one
If you have a strict convention like this for folder name [folder][number] : folder1, folder2, folder3,etc..
You can use this sample to extract the number from the last folder and increase it
const lastFolderName='folder_class123'
/*
You can use match function with a regex like this
It will give you an array with the prefix string, and the number part as a string
*/
const extract=lastFolderName.match(/[a-zA-Z_]+|[0-9]+/g)
console.log(extract)
// You should put some control, like is extract array length =2, etc..
// You can convert the extracted string number of the folder as a number
const lastFolderNumber=parseInt(extract[1])
// Same here you can put some business rule, lastFolderNumber less than 1000
const newFolderNumber=lastFolderNumber+1
console.log(`New folder number : ${newFolderNumber}`)
This should work for any number after the prefix : folder1, folder10, folder222, etc..

Combining multiple xlsx files into a single google sheet for datastudio

I have a folder that will receive multiple xlsx files that will be uploaded via Google forms. There were will be new sheets added a couple of times a week and this data will need to be added.
I want convert all of these xlsx files into a single sheet that will feed a datastudio.
I had started working with this script:
function myFunction() {
//folder ID
var folder = DriveApp.getFolderById("folder ID");
var filesIterator = folder.getFiles();
var file;
var filetype;
var ssID;
var combinedData = [];
var data;
while(filesIterator.hasNext()){
file = filesIterator.next();
filetype = file.getMimeType();
if (filetype === "application/vnd.google-apps.spreadsheet"){
ssID = file.getId();
data = getDataFromSpreadsheet(ssID)
combinedData = combinedData.concat(data);
}//if ends here
}//while ends here
Logger.log(combinedData.length);
}
function getDataFromSpreadsheet(ssID) {
var ss = SpreadsheetApp.openById(ssID);
var ws = ss.getSheets()[0];
var data = ws.getRange("A:W" + ws.getLastRow()).getValues();
return data;
}
Unfortunately that array is returning 0! I think this maybe due to the xlsx issue.
1. Fetch the excel data
Unfortunately, Apps Script can not deal directly with excel values. You need to first convert those files into Google Sheets to access the data. This is fairly easy to do, and can be accomplished using the Drive API (you can check the documentation here) with the following two lines at the top of your code.
var filesToConvert = DriveApp.getFolderById(folderId).getFilesByType(MimeType.MICROSOFT_EXCEL);
while (filesToConvert.hasNext()){ Drive.Files.copy({mimeType: MimeType.GOOGLE_SHEETS, parents: [{id: folderId}]}, filesToConvert.next().getId());}
Please note that this duplicates the existing file by creating a Google Sheets copy of the excel but does not remove the excel file itself. Also note that you will need to activate the Drive API service.
2. Remove duplicates from combinedData
This is not as straightforward as removing duplicate from a regular array, as combinedData is an array of arrays. Nevertheless, it can be accomplished by creating an intermediate object that stores an stringified version of the row array as the key and the row array itself as the value:
var intermidiateStep = {};
combinedData.forEach(row => {intermidiateStep[row.join(":")] = row;})
var finalData = Object.keys(intermidiateStep).map(row=>intermidiateStep[row]);
Extra
I also found another mistake in your code. You should add a 1 (or whichever the first row that you want to read is) when declaring the range of the values to be read, so
var data = ws.getRange("A1:W"+ws.getLastRow()).getValues();
instead of:
var data = ws.getRange("A:W" + ws.getLastRow()).getValues();
As it currently is, Apps Script fails to understand the exact range you want to be read and just assumes that it is the whole page.

Is it possible to store Cytoscape.js layout data directly to a file format in app/web server and re-launch the same layout to minimize re-computation?

Some of the cytoscape layout is randomize where the position is not fix every time we launch it. I understand from multiple stack overflow questions where we can save the layout data (i.e. including its position x and y) into browser local storage or session storage so that we can display the same layout using the same data.
However, the problem with local storage or session storage is good for one users. But, imagine if there are thousands of users using the same app, the server will undergo mass computation for each user to store respective data to individual browsers. Can we save the data into a file format directly into app/web server so that 1000 users will see the same layout and this reduces the computation of different data set as well.
Thank you. Would like to know the possibility to convert data into a file and store in the web/app server.
Yes, you can store position data. Actually, there are 2 options in my mind.
Use cy.json(). You can store the elements as JSON like JSON.stringify(cy.json().elements) and then save this JSON string.
cy.json().elements is something like the below image
You can restore this data easily like cy.json({elements: JSON.parse(jsonStr));
As you could see cy.json().elements is a bit big thing. Position data is just a small object like {x: 0, y: 0}. Additional to position it contains many other data. So if you only need to restore the positions, you could store them manually easily with a code like below. You can use ele.id and node.position() functions.
function storePositions() {
const nodes = cy.nodes();
const nodePositions = {};
for (let i = 0; i < nodes.length; i++) {
nodePositions[nodes[i].id()] = nodes[i].position();
}
return nodePositions;
}
You can also restore node positions easily. You can use getElementById and node.position() functions.
function restorePositions(nodePositions) {
const nodes = cy.nodes();
const nodePositions = {};
for (let k in nodePositions) {
const node = cy.getElementById(k);
if (node && node.length > 0) {
node.position(nodePositions[k]);
}
}
return nodePositions;
}

How can I turn a txt file into a string just using its path?

So I have a program that turns a .txt file into a string to then send it via bluetooth to a printer, the problem is that right now I'm doing it using the file name but I wanted to do it only using the path of the file, this has to do with the fact that I need to search on the folder for any existing txt files and if there are any I need to print the first one and then delete it, so I can't be doing it by using the file's name. This is my code so far:
private fun readFile() String {
val file = File(storage/emulated/0/IS4-PDF-RDP/00233116695912019091310005913BLUETOOTH.txt)
var ins InputStream = file.inputStream()
read contents of IntputStream to String
var content = ins.readBytes().toString(Charset.defaultCharset())
return content
}
You can find the first file in the folder read it and then delete it as per your requirements
File("/storage/emulated/0/IS4-PDF-RDP/").walk().find {
it.extension == "txt"
}?.apply {
inputStream().readBytes().toString(Charset.defaultCharset())
delete()
}

How to write into XML file in Haxe?

I am using Haxe and OpenFL and I got a program that generates a Xml file.
However, I can't figure out how to save that file. I can create Xml tree and check it's valid, but for my life I can't figure out how to write the file.
So, in simple, how to do I write(and create) a file in Haxe? I want to be able to save my newly created Xml File (they serve as sort of settings file and initialization files for my program) on computer, so that I can load it later?
Found the solution right after writing this question.
Solution is to first to use sys.io.File.write() to create the file, then File.saveContent() to save the data in. You can get string from Xml with toString function, the ultimate solution looks like this:
if (!FileSystem.exists("maps"))
{
FileSystem.createDirectory("maps");
}
var number:Int = 1;
if (FileSystem.exists("maps/" + filename + ".xml"))
{
while(FileSystem.exists("maps/" + filename + number +".xml"))
{
number++;
}
filename = filename + number;
}
File.write("maps/" + filename + ".xml", false);
File.saveContent("maps/" + filename + ".xml", root.toString());
This checks if the directory exist and if not, create it and if the file exist, create a new numbered file rather than override it (for the moment, still working on the save feature)
This solution only works on c++, haven't tested others much yet but Flash does not work

Resources