Synchronous Call : Call 2nd function after 1st function is executed completely - node.js

I recently stating coding in node.js and might be a very simple question.
Trying to write a XML parser/validator to validate xml schema and values against values/ xpath stored in an excel sheet.
Now once the validation function is complete I want to call a printResult function to print final result. However if I try to call the function immediately after the first function .. its printing variables initial values and if called within the first which is iterating though the number of xpaths present in excel sheet and printing result with increments.
var mocha = require('mocha');
var assert = require('chai').assert;
var fs = require('fs');
var parseString = require('xml2js').parseString;
var xpath = require('xpath');
var dom = require('xmldom').DOMParser;
var XLSX = require('xlsx');
var Excel = require("exceljs");
var should = require('chai').should();
var HashMap = require('hashmap');
var colors = require('colors');
require('/xmlValidator/dbConnect.js');
var map = new HashMap();
var elementMap = new HashMap();
var resultValue;
//console.log('hello'.green);
map.set("PASS", 0);
map.set("FAIL", 0);
map.set("INVALID_PATH", 0);
function computeResult(elementPath, result) {
var pass = map.get("PASS");
var fail = map.get("FAIL");
var invalidPath = map.get("INVALID_PATH");
elementMap.set(elementPath, result);
if (result == "PASS") {
pass++;
map.set("PASS", pass);
} else if (result == "FAIL") {
fail++;
map.set("FAIL", fail);
} else {
invalidPath++;
map.set("INVALID_PATH", invalidPath)
}
printResult();
}
function printResult() {
var pass = map.get("PASS");
var fail = map.get("FAIL");
var invalidPath = map.get("INVALID_PATH");
console.log(("PASS Count :" + pass).green);
console.log(("FAIL Count :" + fail).red);
console.log(("Inavlid Path :" + invalidPath).yellow);
elementMap.forEach(function(value, key) {
if (value == "INVALID_PATH")
console.log((key + ":" + value).yellow);
else if (value == "FAIL")
console.log((key + ":" + value).red);
else
console.log(key + ":" + value);
});
}
var workbook = new Excel.Workbook();
workbook.xlsx.readFile('utils/' + process.argv[2])
.then(function() {
var worksheet = workbook.getWorksheet(1);
worksheet.eachRow(function(row, rowNumber) {
//console.log(rowNumber);
var row = worksheet.getRow(rowNumber);
var dataPath1 = row.getCell("A").value;
var dataPath2 = row.getCell("B").value;
var dataPath = dataPath1 + dataPath2;
//console.log(dataPath);
var dataValue = row.getCell("D").value;
var flag = row.getCell("E").value;
//console.log(flag)
//console.log(dataValue);
if (!flag)
validate(dataPath, dataValue, rowNumber);
//else console.log("NOT EXECUTED" + rowNumber)
});
})
function validate(dataPath, dataValue, rowNumber) {
var fail = 0;
fs.readFile('utils/' + process.argv[3], 'utf8', function(err, data) {
if (err) {
console.log("ERROR ERROR ERROR ERROR ");
return console.log(err);
}
var doc = new dom().parseFromString(data);
var subId = String(xpath.select1(dataPath, doc));
if (subId == "undefined") {
/*console.log('undefined caught');
console.log("row number :" + rowNumber);*/
var resultValue = "INVALID_PATH";
computeResult(dataPath, resultValue);
} else {
var subId = xpath.select1(dataPath, doc);
var value = subId.lastChild.data;
/*console.log("row number :" + rowNumber);
console.log("actual value: " + value);
console.log("expected value:" + dataValue );*/
if (dataValue == null) {
assert.notEqual(value, dataValue, "value not found");
resultValue = "PASS";
computeResult(dataPath, resultValue);
} else {
if (value == dataValue)
resultValue = "PASS";
else resultValue = "FAIL";
computeResult(dataPath, resultValue);
}
}
});
}
In the code above i want to call printResult() function after validate function is completely executed (workbook.xlsx.readFile)
Can some one please help me out how to use done () function or make sync call ?

If fs.readFileAsync('utils/' + process.argv[3], 'utf8') can be executed once, then validate() will be completely synchronous and calling printResult() after the verification loop will be trivial.
In the main routine, you can simply aggregate two promises ...
var promise1 = workbook.xlsx.readFile();
var promise2 = fs.readFileAsync(); // requires `fs` to be promisified.
... before embarking on the verification loop.
Promise.all([promise1, promise2]).spread(/* verify here */);
Also a whole bunch of tidying can be considered, in particular :
establishing "PASS", "FAIL" and "INVALID_PATH" as constants to avoid lots of repetitive string creation,
using js plain objects in lieu of hashmap,
building map from elementMap inside the print function.
having validate() return its result and building elementMap in the main routine
Here's the whole thing, about as tight as I can get it. I may have made a few assumptions but hopefully not too many bad ones ...
var mocha = require('mocha');
var assert = require('chai').assert;
var Promise = require("bluebird");
var fs = Promise.promisifyAll(require("fs")); // allow Bluebird to take the pain out of promisification.
var parseString = require('xml2js').parseString;
var xpath = require('xpath');
var dom = require('xmldom').DOMParser;
var XLSX = require('xlsx');
var Excel = require("exceljs");
var should = require('chai').should();
// var HashMap = require('hashmap');
var colors = require('colors');
require('/xmlValidator/dbConnect.js');
const PASS = "PASS";
const FAIL = "FAIL";
const INVALID_PATH = "INVALID_PATH";
function printResult(elementMap) {
var key, result,
map = { PASS: 0, FAIL: 0, INVALID_PATH: 0 },
colorNames = { PASS: 'black', FAIL: 'red', INVALID_PATH: 'yellow' };
for(key in elementMap) {
result = elementMap[key];
map[(result === PASS || result === FAIL) ? result : INVALID_PATH] += 1;
console.log((key + ": " + result)[colorNames[result] || 'black']); // presumably colors can be applied with associative syntax? If so, then the code can be very concise.
}
console.log(("PASS Count: " + map.PASS)[colorNames.PASS]);
console.log(("FAIL Count: " + map.FAIL)[colorNames.FAIL]);
console.log(("Inavlid Path: " + map.INVALID_PATH)[colorNames.INVALID_PATH]);
}
function validate(doc, dataPath, dataValue) {
var subId = xpath.select1(dataPath, doc),
value = subId.lastChild.data,
result;
if (String(subId) == "undefined") {
result = INVALID_PATH;
} else {
if (dataValue === null) {
assert.notEqual(value, dataValue, "value not found"); // not too sure what this does
result = PASS;
} else {
result = (value === dataValue) ? PASS : FAIL;
}
}
return result;
}
//Main routine
var workbook = new Excel.Workbook();
var promise1 = workbook.xlsx.readFile('utils/' + process.argv[2]); // from the question, workbook.xlsx.readFile() appears to return a promise.
var promise2 = fs.readFileAsync('utils/' + process.argv[3], 'utf8');
Promise.all([promise1, promise2]).spread(function(data2, data3) {
var worksheet = workbook.getWorksheet(1),
doc = new dom().parseFromString(data3),
elementMap = {};
worksheet.eachRow(function(row, rowNumber) {
// var row = worksheet.getRow(rowNumber); // row is already a formal variable ???
var dataPath, dataValue;
if (!row.getCell('E').value)
dataPath = row.getCell('A').value + row.getCell('B').value;
dataValue = row.getCell('D').value;
elementMap[dataPath] = validate(doc, dataPath, dataValue);
});
printResult(elementMap);
});
Untested so may not run but at least you can raid the code for ideas.

Related

Edit a JSON object

I retrieved a JSON object from a local database, I want to edit a value (invItems) and add a new value to it (filed[filed.invItems]), then upload it back to the database, but it does not seem to work (the JSON does not seem to change)
async function invPut(itemID, message) {
var filed = await frenzyDB.getKey(id + "_invcache");
console.log("Before: " + filed)
newInvItems = filed.invItems + 1;
filed.invItems = newInvItems;
filed[filed.invItems] = itemID;
console.log("After: " + filed);
await frenzyDB.addKey(id + "_invcache", filed)
}
Console Output:
Before: {"invItems":0}
After: {"invItems":0}
It shows no errors, but the JSON doesnt change. Am I doing something wrong? If so, what can I do to fix it?
Thanks for all your help!
Notes:
frenzyDB is just a javascript file that deals with a standard REPL.it Database
Code of frenzyDB:
const Database = require("#replit/database")
const db = new Database()
async function addKey(key, value) {
await db.set(key, value).then(() => {return;});
}
async function getKey(key) {
return await db.get(key).then(value => {return value;});
}
function listAllKeys() {
db.list().then(keys => {return keys;});
}
async function hasKey(key) {
var keys = await listAllKeys();
if (keys.includes(key)) {
return true;
} else {
return false;
}
}
async function removeKey(key) {
await db.delete(key).then(() => {return;});
}
module.exports = {
addKey,
getKey,
listAllKeys,
hasKey,
removeKey
};
Edit: Latest code:
async function invPut(itemID, message) {
await init(message.author.id);
var filed = await frenzyDB.getKey(message.author.id + "_invcache");
console.log(filed)
const result = {};
result.invItems = (filed['invItems'] + 1) || 1;
result.hasOwnProperty(filed.invItems) ? result[filed.invItems + 1] = itemID : result[filed.invItems] = itemID;
console.log(result);
frenzyDB.addKey(message.author.id + "_invcache", result)
message.reply("A **"+ itemIDs[itemID].name + "** was placed in your inventory");
return true;
}
EDIT 2: Latest Console Output:
{ '4': 3, invItems: 5 }
{ '5': 3, invItems: 6 }
Any help will be appreciated!
Thanks
Try this
// Demo Data
const itemID = 10;
var filed = { "invItems" : 0 };
// Real function
console.log("Before: " + JSON.stringify(filed));
const result = {};
result.invItems = (filed['invItems'] + 1) || 1;
result.hasOwnProperty(filed.invItems) ? result[filed.invItems + 1] = itemID : result[filed.invItems] = itemID;
console.log("After: " + JSON.stringify(result));
The result I get is
Before: {"invItems":0}
After: {"0":10,"invItems":1}
You would then of course use result to store the data away in the DB.
async function invPut(itemID, message) {
// Typo?
var filed = await frenzyDB.getKey(itemID + "_invcache");
console.log("Before: " + filed)
const result = {};
result.invItems = (filed['invItems'] + 1) || 1;
result.hasOwnProperty(filed.invItems) ? result[filed.invItems + 1] = itemID : result[filed.invItems] = itemID;
console.log("After: " + result);
// Typo?
await frenzyDB.addKey(itemID + "_invcache", result)
}
Answer Edit:
const result = { ...filed };
result.invItems = (filed['invItems'] + 1) || 1;
result.hasOwnProperty(filed.invItems) ? result[filed.invItems + 1] = itemID : result[filed.invItems] = itemID;
console.log(JSON.stringify(result));
maybe this will help you
const json = fs.readFileSync(`${__dirname}/data/data.json`, "utf-8");
const inputData = JSON.parse(json);
inputData.push({input: 'front'}) // creates new element for data.json
-------------------------------------------
array.push({front: 'front', back: 'back'});

Problem with findOne() in sequelize node.js

I have a problem with node.js and sequelize findOne(). I want to find new students, that I want to add to the DB (var novi), and the ones that already exist, I just want to update their field (var stari). Everything works as expected, only when I want to return JSON with how many new students I added to the DB, and how many are updated, values of stari and novi, go back to 0, but the counting is good, I checked. I know the problem is with asynchronous call, but I don't know how to fix.
app.post('/student', function(req,res) {
var imeGodine = req.body['godina'];
//POMOĆNE SKRIPTE BitBucket.js i citanjeGodina.js
var broj = 0;
var stari = 0;
var novi = 0;
db.godina.findOne({where:{nazivGod:req.body.godina}}).then(god => {
var studenti = req.body.studenti;
db.student.count().then (ranijeStudenata => {
for(var i = 0; i<studenti.length; i++) {
var ime = studenti[i].imePrezime;
var ind = studenti[i].index;
db.student.findOne({where:{index :studenti[i].index}}).then(stud => {
if (stud == null) {
novi++;
db.student.create({imePrezime:ime, index : ind}).then(noviStudent => {
god.addStudenti(noviStudent);
});
}
else if (stud != null) {
stari++;
god.addStudenti(stud);
}
});
broj++;
}
var brojNovih = broj - ranijeStudenata; //ne koristi se, ali možda hoće
res.set("Content-Type", "application/json");
res.status(200).send(JSON.stringify({message: "Dodano je " + novi + " novih studenata i upisano " + stari + " na godinu " + imeGodine}));
});
});
});
Picture of code
You can use async/await to do counting in a synchronous way.
'use strict';
app.post('/student', async function (req, res) {
var imeGodine = req.body['godina'];
var {studenti} = req.body;
var broj = 0;
var stari = 0;
var novi = 0;
let god = await db.godina.findOne({where: {nazivGod: req.body.godina}});
let ranijeStudenata = await db.student.count(); // ranijeStudenata not used?
for (var i = 0; i < studenti.length; i++) {
var ime = studenti[i].imePrezime;
var ind = studenti[i].index;
let stud = await db.student.findOne({where: {index: studenti[i].index}});
if (stud === null) {
novi++;
let noviStudent = await db.student.create({imePrezime: ime, index: ind});
god.addStudenti(noviStudent);
} else if (stud !== null) {
stari++;
god.addStudenti(stud);
}
broj++;
}
return res.status(200).send({
message: "Dodano je " + novi + " novih studenata i upisano " + stari + " na godinu " + imeGodine
});
});

NodeJS Script Runs on Local Machine but Not in Firebase Cloud Functions?

My script written in NodeJS connects to Firebase, checks my Firebase Database and even successfully sends notifications when results from my database return true... However, it only works when I run it from my local machine. I deploy it to Firebase and it will not work. Could someone please advise? Thank you.
I hate asking on here because I'm a newbie but I have spent hours tonight trying to find an answer...
INDEX.JS
// Firebase Functions
const functions = require('firebase-functions');
var admin = require("firebase-admin");
// Default admin firebase configuration
admin.initializeApp(functions.config().firebase);
// var serviceAccount = require("xxxxxx-80xxxxd-firebase-adminsdk- xxxxxxx.json");
var moment = require('moment');
var FCM = require('fcm-push');
var dateTime = require('node-datetime');
var serverKey = 'xxxxxxxxxxxxxpSELZBjQYwpZgmxxxxxxxxxxx';
var fcm = new FCM(serverKey);
//Initial function call:
exports.CheckDates = functions.https.onRequest((req, response) => {
// Get a database reference to our posts
var db = admin.database();
var ref = db.ref("records");
var userToken = '';
var itemExpires = '';
var itemName = '';
var reminded = '';
var itemCount = 0;
var counter = 1;
var itemFoundCount = 0;
var dt = dateTime.create();
var formatted = dt.format('m-d-Y');
ref.once("value", function (recordsSnapshot) {
recordsSnapshot.forEach(function (recordsSnapshot) {
var mainKey = recordsSnapshot.key;
recordsSnapshot.forEach(function (child) {
var key = child.key;
var value = child.val();
if (key == 'Account') {
userToken = value.userToken;
}
if (key == 'Items') {
recordsSnapshot.child("Items").forEach(function (itemsSnapshot) {
counter++;
if (itemFoundCount === 0) {
itemFoundCount = itemsSnapshot.numChildren();
}
var itemsChildkey = itemsSnapshot.key;
var itemsChildvalue = itemsSnapshot.val();
itemExpires = itemsChildvalue.itemExpires;
itemName = itemsChildvalue.itemName;
reminded = itemsChildvalue.reminded;
moment().format('YYYY-MM-DD');
var currentDate = moment();
var otherTime = moment(reminded);
if (typeof reminded !== 'undefined') {
if (currentDate.diff(otherTime, 'days') >= 30) {
if (currentDate.diff(itemExpires, 'days') <= 90) {
itemCount++;
console.log("Expire date is less than " +
currentDate + " by 90 days = " + (currentDate.diff(otherTime, 'days') <=
90));
db.ref("records/" + mainKey + "/Items/" +
itemsChildkey + '/reminded').set(formatted);
}
}
} else {
itemCount++;
db.ref("records/" + mainKey + "/Items/" + itemsChildkey +
`enter code here`'/reminded').set(formatted);
}
if (counter == itemFoundCount && itemCount > 0) {
console.log(itemFoundCount);
var message = {
to: userToken, // required fill with device token or
topics
notification: {
title: 'Item Expire Notification',
body: itemCount + ' is about to expire.'
}
};
//callback style
fcm.send(message, function (err, response) {
if (err) {
console.log("Something has gone wrong!");
} else {
console.log("Successfully sent with response: ",
response);
}
});
itemCount = 0;
itemFoundCount = 0;
counter = 1;
}
});
}
});
});
});
response.send(200, "ok");
}) // END exports.CheckDates
Obviously, I remove the Exports.CheckDates lines when I run it locally but it wasn't showing up at all without the exports on Firebase's console. It returns warnings locally and on Firebase console but it works on one and not the other.
Please disregard this question. The script adds the date that the last time a notification was sent to the item's "Reminded" key in the database... this prevents notifications from going out every day for the same thing..
It ran on my local machine, and did it's job but I couldn't figure out why it wouldn't run again... well, ha! At least we know that part works.

Making an async loop in node.js with sql

Here is what I'm trying to do:
Start a looping (10x)
Select on sql to return 1 register (select top 1 where 'running' is null)
Sql update 'running' status to 'running'
If the record is null, I access an API and get some data
The result is updated on the initial sql record (set running = 'ok')
End looping (start over)
Thing is, node.js does not wait for start over, it does everything at the same time. That way, 'running' is always null.
var express = require('express');
var app = express();
var c_MyApi = require('./controller/call_MyApi');
var mongo = require('./controller/crud_mongo');
var c_email = require('./controller/api_email_verify');
var c_sql = require('./controller/consulta_sql');
var MyLink = '',
id = 0;
for( var i = 0 ; i < 10 ; i++){
c_sql.busca().then(function(res) {
MyLink = res[0].MyLink;
id = res[0].id;
c_sql.marca(id).then(
c_MyApi.busca(MyLink, function(a) {
if (a == 0) {
c_sql.atualiza(id, 'sem_email', 's/e');
}
if (a == 1) {
c_sql.atualiza(id, 'link_errado', 'l/e');
} else {
for (var i = 0; i < a.length; i++) {
var email = a[i].address;
c_email.busca(email, function(e_valid) {
c_sql.atualiza(id, email, e_valid)
})
}
}
})
)
})
}
}
//consulta_sql.js
var sql = require("seriate");
var search = function() {
var exec = 'select top 1 MyLink, id from sys_robo_fila where done is null';
sql.setDefaultConfig(config);
return sql.execute({
query: exec
});
}
var usado = function(id) {
var exec = "update sys_robo_fila set done = 'r' where id = " + id + "";
sql.setDefaultConfig(config);
return sql.execute({
query: exec
});
}
var update = function(id, email, valid) {
var exec = "update sys_robo_fila set email = '" + email + "' , validacao = '" + valid + "' , done = 'ok' where id = " + id + "";
sql.setDefaultConfig(config);
return sql.execute({
query: exec
});
}
module.exports = {
busca: search,
atualiza: update,
marca: usado
}
Any sugestions?
The call to c_sql.busca() is returning a Promise immediately and then continuing on to the next loop before then() is called, which is why they seem to run at the same time (they are actually just running very quickly, but before the Promise is resolved.
If you want this to run synchronously, one loop at a time, I would recommend using a recursive function to not start the loop again until the Promises have resolved.
let count = 0;
function doSomething() {
// this returns immediately, before .then() executes
return c_sql.busca()
.then(() => {
// do some more stuff after c_sql.busca() resolves
return c_sql.busca();
})
.then(() => {
// increment your count
count += 1
if (count<10) {
// less than 10 runs call the function again to start over
return doSomething();
}
});
}
This article might be helpful in understanding Promises: https://medium.com/#bluepnume/learn-about-promises-before-you-start-using-async-await-eb148164a9c8

Google Apps Script: Save Spreadsheet as ODS for Local Backup

I could use a hand. My company uses Google Sheets extensively, and we need a way to access files when we lose our Internet connection.
I could not get any of the examples found on this site to work for creating xls or ods from Google Sheets via script.
I did script a way to create csv backups, accessible from a local Google Drive folder. When used with an hourly trigger, this script creates csv files of every sheet of any spreadsheet modified in the last hour, puts them in a folder, and zips it inside a folder specifically for backups. From there I can move the zip to our local server.
function backUpMaker() {
var backupFolderId = '<Id of Backup Folder>';
var timeNow = new Date();
var newFolder = DocsList.createFolder(timeNow);
var newFolderId =newFolder.getId();
newFolder.addToFolder(DocsList.getFolderById(backupFolderId));
newFolder.removeFromFolder(DocsList.getRootFolder());
var sheets = DriveApp.getFilesByType(MimeType.GOOGLE_SHEETS);
while (sheets.hasNext()) {
var sheet = sheets.next();
var lastUpdate = sheet.getLastUpdated();
var timeHourAgo = new Date();
timeHourAgo = timeNow - 3600000;
if(lastUpdate >= timeHourAgo){
var sheetId = sheet.getId();
var csv = eachSheet(sheetId,newFolderId);
}
}
var backupFolder = DocsList.getFolderById(backupFolderId);
try{
backupFolder.createFile(Utilities.zip(newFolder.getFiles(), timeNow + '.zip'));
} catch(err) {
Logger.log(err);
}
newFolder.setTrashed(true);
}
function eachSheet(key,newFolderId) {
var ss = SpreadsheetApp.openById(key);
var ssId = ss.getId();
var ssName = ss.getName();
var howManySheets = ss.getNumSheets();
try{
for (var sheetIndex=0; sheetIndex < howManySheets; sheetIndex++) {
var activeSheet = ss.getSheets()[sheetIndex];
var activeName = activeSheet.getName();
if(activeName != 'Dropdowns'){ //Skip a hidden sheet used for validation on many of our spreadsheets
var activeId = activeSheet.getSheetId();
var time = new Date();
var fileName = time + " Backup: " + ssName + " " + activeName + ".csv";
var csv = contentCSV(ssId,activeId);
var folder = DocsList.getFolderById(newFolderId);
folder.createFile(fileName, csv, 'text/plain');
}
}
} catch(err) {
Logger.log(err)
}
}
function contentCSV(key,gid) {
var file = DocsList.getFileById(key);
var response = UrlFetchApp.fetch("https://spreadsheets.google.com/feeds/download/spreadsheets/Export?key=" + key +
"&gid=" + gid + "&exportFormat=csv", oAuth());
var fileText = response.getContentText();
return fileText;
}
function oAuth() {
var oauthConfig = UrlFetchApp.addOAuthService("spreadsheets");
var scope = "https://spreadsheets.google.com/feeds"
oauthConfig.setConsumerKey("anonymous");
oauthConfig.setConsumerSecret("anonymous");
oauthConfig.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken?scope="+scope);
oauthConfig.setAuthorizationUrl("https://accounts.google.com/OAuthAuthorizeToken");
oauthConfig.setAccessTokenUrl("https://www.google.com/accounts/OAuthGetAccessToken");
var requestData = {
"oAuthServiceName": "spreadsheets",
"oAuthUseToken": "always",
};
return requestData;
}
How would I modify this to save as ods rather than csv? Or is there a better way to save backups? Thank you for your help!
Working code follows, thanks to input by #serge-insas :
Note: To make this work, enter your backup folder's id key, then choose ods or xlsx by commenting out the other choice. Then you need to set up a timed hourly trigger for backUpMaker(). Logs will catch errors for files that have been moved temporarily. Here goes:
function backUpMaker() {
var backupFolderId = '0B5--------------------1ZX1k';
//var exportFormat = 'ods';
var exportFormat = 'xlsx';
var timeNow = new Date();
var newFolder = DocsList.createFolder(timeNow);
var newFolderId =newFolder.getId();
newFolder.addToFolder(DocsList.getFolderById(backupFolderId));
newFolder.removeFromFolder(DocsList.getRootFolder());
var sheets = DriveApp.getFilesByType(MimeType.GOOGLE_SHEETS);
while (sheets.hasNext()) {
var sheet = sheets.next();
var lastUpdate = sheet.getLastUpdated();
var timeHourAgo = new Date();
timeHourAgo = timeNow - 3600000;
if(lastUpdate >= timeHourAgo){
try{
var key = sheet.getId();
var name = sheet.getName();
var fileName = timeNow + " Backup: " + name + "." + exportFormat;
var blob = contentBackup(key,exportFormat);
var folder = DocsList.getFolderById(newFolderId);
folder.createFile(blob).rename(fileName);
} catch(err){
Logger.log(err);
}
}
}
var backupFolder = DocsList.getFolderById(backupFolderId);
try{
backupFolder.createFile(Utilities.zip(newFolder.getFiles(), timeNow + '.zip'));
} catch(err) {
Logger.log(err);
}
newFolder.setTrashed(true);
}
function contentBackup(key,exportFormat) {
try{
if(exportFormat == 'xlsx'){
exportFormat = 'xls';
}
var file = DocsList.getFileById(key);
var response = UrlFetchApp.fetch("https://spreadsheets.google.com/feeds/download/spreadsheets/Export?key=" + key +
"&exportFormat=" + exportFormat, oAuth());
var fileBlob = response.getBlob();
return fileBlob;
} catch(err) {
Logger.log(err)
}
}
function oAuth() {
var oauthConfig = UrlFetchApp.addOAuthService("spreadsheets");
var scope = "https://spreadsheets.google.com/feeds"
oauthConfig.setConsumerKey("anonymous");
oauthConfig.setConsumerSecret("anonymous");
oauthConfig.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken?scope="+scope);
oauthConfig.setAuthorizationUrl("https://accounts.google.com/OAuthAuthorizeToken");
oauthConfig.setAccessTokenUrl("https://www.google.com/accounts/OAuthGetAccessToken");
var requestData = {
"oAuthServiceName": "spreadsheets",
"oAuthUseToken": "always",
};
return requestData;
}
Your code needed very few changes to get ods files.
2 differences : the requested format in the urlFetch becomes "ods" and the returned object is not a string anymore but a blob.
Beside that, I kept all the sheet selection functions that you wrote and it seems to work fine as well. (nice job you did !).
Below is the full code I reproduced from yours and changed as described. My tests were working fine.
function backUpMaker() {
var backupFolderId = '0B3qS--------------TXdlY2M';
var timeNow = new Date();
var newFolder = DocsList.createFolder(timeNow);
var newFolderId =newFolder.getId();
newFolder.addToFolder(DocsList.getFolderById(backupFolderId));
newFolder.removeFromFolder(DocsList.getRootFolder());
var sheets = DriveApp.getFilesByType(MimeType.GOOGLE_SHEETS);
while (sheets.hasNext()) {
var sheet = sheets.next();
var lastUpdate = sheet.getLastUpdated();
var timeHourAgo = new Date();
timeHourAgo = timeNow - 3600000;
if(lastUpdate >= timeHourAgo){
var sheetId = sheet.getId();
var csv = eachSheet(sheetId,newFolderId);
}
}
var backupFolder = DocsList.getFolderById(backupFolderId);
try{
backupFolder.createFile(Utilities.zip(newFolder.getFiles(), timeNow + '.zip'));
} catch(err) {
Logger.log(err);
}
newFolder.setTrashed(true);
}
function eachSheet(key,newFolderId) {
var ss = SpreadsheetApp.openById(key);
var ssId = ss.getId();
var ssName = ss.getName();
var howManySheets = ss.getNumSheets();
try{
for (var sheetIndex=0; sheetIndex < howManySheets; sheetIndex++) {
var activeSheet = ss.getSheets()[sheetIndex];
var activeName = activeSheet.getName();
if(activeName != 'Dropdowns'){ //Skip a hidden sheet used for validation on many of our spreadsheets
var activeId = activeSheet.getSheetId();
var time = new Date();
var fileName = time + " Backup: " + ssName + " " + activeName + ".ods";
var blob = contentODS(ssId,activeId);
var folder = DocsList.getFolderById(newFolderId);
folder.createFile(blob).rename(fileName);
}
}
} catch(err) {
Logger.log(err)
}
}
function contentODS(key,gid) {
var file = DocsList.getFileById(key);
var response = UrlFetchApp.fetch("https://spreadsheets.google.com/feeds/download/spreadsheets/Export?key=" + key +
"&gid=" + gid + "&exportFormat=ods", oAuth());
var fileBlob = response.getBlob();
return fileBlob;
}
function oAuth() {
var oauthConfig = UrlFetchApp.addOAuthService("spreadsheets");
var scope = "https://spreadsheets.google.com/feeds"
oauthConfig.setConsumerKey("anonymous");
oauthConfig.setConsumerSecret("anonymous");
oauthConfig.setRequestTokenUrl("https://www.google.com/accounts/OAuthGetRequestToken?scope="+scope);
oauthConfig.setAuthorizationUrl("https://accounts.google.com/OAuthAuthorizeToken");
oauthConfig.setAccessTokenUrl("https://www.google.com/accounts/OAuthGetAccessToken");
var requestData = {
"oAuthServiceName": "spreadsheets",
"oAuthUseToken": "always",
};
return requestData;
}

Resources