I need to call several time an asynchronous function (here, to force motor speed), until maximum speed is reached.
But I don't know how to code "multiple calls" with no predefined number of call.
Here is the base of my code.
/* drive motor
from initialSpeedPercent
to finalSpeedPercent
step by step with delay
*/
var initialSpeedPercent = 0;
var finalSpeedPercent = 80;
var duration = 2000; // in millisecond
var accelerationStep = 5;
var stepSpeed = (finalSpeedPercent - initialSpeedPercent) / (accelerationStep - 1);
var stepDuration = duration / (accelerationStep - 1);
function setMotorSpeed( percentOfSpeed, funcWhenCompleted) {
// send consign to motor then return
console.log( `motorspeed set to ${percentOfSpeed}`);
let err = undefined; // depending of motor behavior
if (err) {
funcWhenCompleted( { cmd: 'setMotorSpeed', err: err }, undefined );
}
else {
funcWhenCompleted( undefined, { cmd: 'setMotorSpeed', newSpeed: percentOfSpeed } );
}
}
const promiseSpeed = new Promise( function ( resolve, reject ) {
initialSpeedPercent += stepSpeed;
setMotorSpeed( initialSpeedPercent, function (err, result) {
if (err) {
reject( err);
} else {
// resolve after step duration
setTimeout( () => {
resolve( result );
}, stepDuration );
}
} );
} );
promiseSpeed
.then( ( result ) => {
console.log( 'promiseSpeed success:', result );
... how to call in cascade.... until result.newSpeed >= finalSpeedPercent ?
promiseSpeed
.then( ( result ) => {
console.log( 'promiseSpeed success:', result );
} );
} )
.catch( ( error ) => {
console.log( 'promiseSpeed error:', error );
} )
.finally( () => {
console.log('promiseSpeed() is done!');
} );
How to do that ?
I have found this solution. But I suppose there is a more simple way to do that.
How to avoid a dangerous stack overflow if there are to much inside-call ?
/* _testCascade.js
- Drive motor speed principle, step by step
*/
Date.prototype.yyyymmddhhmmsslll = function() {
var yyyy = this.getFullYear();
var mm = this.getMonth() < 9 ? "0" + (this.getMonth() + 1) : (this.getMonth() + 1); // getMonth() is zero-based
var dd = this.getDate() < 10 ? "0" + this.getDate() : this.getDate();
var hh = this.getHours() < 10 ? "0" + this.getHours() : this.getHours();
var min = this.getMinutes() < 10 ? "0" + this.getMinutes() : this.getMinutes();
var ss = this.getSeconds() < 10 ? "0" + this.getSeconds() : this.getSeconds();
var lll = this.getMilliseconds() < 10 ? "00" + this.getMilliseconds() :
this.getMilliseconds() < 100 ? "0" + this.getMilliseconds() :
this.getMilliseconds();
return yyyy + '-' + mm + '-' + dd + ' ' + hh + ':' + min + ':' + ss + '.' + lll;
};
/* #params: { fromSpeed: -50, stepSpeed: +10, toSpeed: -12 }
speed(s) in percent, from -100 to +100
*/
function driveMotor( params) {
const funcName = "driveMotor";
// console.log( `${new Date()} > ${funcName} > enter with params: ${JSON.stringify(params)}...`);
return new Promise((successCallback,failureCallback) => {
let objective = params.fromSpeed + params.stepSpeed;
// TIMEOUT simulate asynchronous command of speed
setTimeout( function() {
const funcName = "asyncDriveMotor";
// console.log( `${new Date()} > ${funcName} > command executed with objective: ${objective}...`);
if (Math.random() > .2) {
// if (true) {
// console.log( `${new Date()} > ${funcName} > success with objective: ${objective}...`);
let success = params;
success.reachedSpeed = objective;
successCallback( { err: undefined, success: success } );
} else {
// console.log( `${new Date()} > ${funcName} > Promise FAILS with objective: ${objective}...`);
failureCallback( { err: true, success: undefined} );
}
}, 100);
})
}
function progressiveDriveMotorSpeed( params) {
if (params.stepSpeed == 0) {
return;
}
if (params.fromSpeed < -100
|| params.fromSpeed > +100) return;
if (params.toSpeed < -100
|| params.toSpeed > +100) return;
let promise = driveMotor( params );
promise.then( function(result) {
const funcName = "driveMotor.then";
let stop = false;
console.log( `${new Date()} > ${funcName} > result: ${JSON.stringify(result)} `);
if (result.err) {
console.log( `${new Date()} > ${funcName} > err: ${result.err}...stop required`);
stop = true;
} else {
// console.log( `${new Date()} > ${funcName} > success...`);
if (result.success.stepSpeed > 0) {
if (result.success.reachedSpeed >= result.success.toSpeed) {
stop = true;
}
}
if (result.success.stepSpeed < 0) {
if (result.success.reachedSpeed <= result.success.toSpeed) {
stop = true;
}
}
// console.log( `${new Date()} > ${funcName} > success 2...stop: ${stop}`);
if (stop == false) {
params.fromSpeed = result.success.reachedSpeed;
if ( (params.fromSpeed + params.stepSpeed) > params.toSpeed) {
params.fromSpeed = params.toSpeed - params.stepSpeed;
console.log( `${new Date()} > ${funcName} > adjust in order to reach exactly...params.fromSpeed: ${params.fromSpeed}`);
}
// recall
progressiveDriveMotorSpeed( params);
}
}
})
.catch( function( result) {
const funcName = "catch";
if (result.err) {
console.log( `${new Date()} > ${funcName} > FAILURE...result: ${JSON.stringify(result)} with params: ${JSON.stringify( params)} `);
} else {
console.log( `${new Date()} > ${funcName} > COMPLETED...result: ${JSON.stringify(result)} with params: ${JSON.stringify( params)}`);
}
});
}
/* tests:
progressiveDriveMotorSpeed( { fromSpeed: 0, stepSpeed: 10, toSpeed: 50 } );
progressiveDriveMotorSpeed( { fromSpeed: 0, stepSpeed: 10, toSpeed: 48 } );
progressiveDriveMotorSpeed( { fromSpeed: 50, stepSpeed: -10, toSpeed: 0 } );
progressiveDriveMotorSpeed( { fromSpeed: -50, stepSpeed: +10, toSpeed: -12 } );
*/
progressiveDriveMotorSpeed( { fromSpeed: -50, stepSpeed: +10, toSpeed: -12 });
Related
I am having a extension where it can notify about new items added to RSS feed reader
All works in v2, but v3 I am unable to load jquery into service worker, since chrome doesnt allow.
As a workaround I have added as module
"background": {
"service_worker": "js/background.js","type": "module"
},
But still its a issue and says ReferenceError: $ is not defined
at Object.parseFeed
Or is there a way I can tweak my code to read xml without jquery?
import $ as module from 'js/jquery-2.1.4.min.js';
var Storage = (function() {
return {
getFeeds: function(callback, returnArray, sortArray) {
returnArray = typeof returnArray !== 'undefined' ? returnArray : true;
sortArray = typeof sortArray !== 'undefined' ? sortArray : true;
chrome.storage.sync.get(function(dataObject) {
var result = dataObject;
if (returnArray) {
var feedArray = this.parseDataObjectIntoArray(dataObject);
result = sortArray ? this.sortArrayOfObjects(feedArray, 'position') : feedArray;
} else {
delete result['RssR:Settings'];
}
callback(result)
}.bind(this));
},
getFeedByUrl: function(feedUrl, callback) {
chrome.storage.sync.get(feedUrl, function(feedData) {
callback(feedData[feedUrl]);
});
},
removeFeedByUrl: function(feedUrl) {
chrome.storage.sync.remove(feedUrl);
},
saveFeedData: function(feedData) {
var saveFeed = {};
saveFeed[feedData.url] = feedData;
this.setDataObject(saveFeed);
},
parseDataObjectIntoArray: function(object) {
var array = [];
Object.keys(object).forEach(function(objectKey) {
if (objectKey.indexOf('RssR:Settings') !== 0) {
array.push(object[objectKey]);
}
});
return array;
},
sortArrayOfObjects: function(array, sortKey) {
array.sort(function(a, b) {
if (typeof a[sortKey] === 'undefined') {
return true;
} else if (typeof b[sortKey] === 'undefined') {
return false;
}
return a[sortKey] - b[sortKey];
});
return array;
},
clearAllData: function() {
chrome.storage.sync.clear();
},
setDataObject: function(dataObject) {
chrome.storage.sync.set(dataObject);
}
}
}());
Array.prototype.max = function() {
return Math.max.apply(null, this);
};
function msToTime(ms) {
let seconds = (ms / 1000).toFixed(1);
let minutes = (ms / (1000 * 60)).toFixed(1);
let hours = (ms / (1000 * 60 * 60)).toFixed(1);
let days = (ms / (1000 * 60 * 60 * 24)).toFixed(1);
if (seconds < 60) return seconds + " Sec";
else if (minutes < 60) return minutes + " Min";
else if (hours < 24) return hours + " Hrs";
else return days + " Days"
}
var FeedService = (function() {
return {
downloadFeed: function(feed) {
if (!feed.url) {
return;
}
console.log(feed.url)
fetch(feed.url)
.then(response => response.text())
.then(xmlString => {
console.log(xmlString)
this.parseFeed(xmlString, feed);
}
)
.then(data => console.log(data))
},
parseFeed: function(rawData, existingFeedData) {
var newFeedData = {};
var $feedEntries = $(rawData).find("entry").length > 0 ? $(rawData).find("entry") : $(rawData).find('item');
var lastUpdate = Date.now()
console.log("Now = " + lastUpdate)
lastUpdate = (existingFeedData && existingFeedData.lastUpdate) || Date.now()
console.log("Last Update = " + lastUpdate)
console.log("existingFeedData = " + JSON.stringify(existingFeedData))
pubDates = [];
$feedEntries.each(function(index, elm) {
pubDates.push(new Date($(elm).find("pubDate").text()).getTime())
if (lastUpdate < new Date($(elm).find("pubDate").text()).getTime()) {
console.log($(elm).find("title").text().substring(0, 20));
chrome.notifications.create(
$(elm).find("link").text(), {
type: "basic",
iconUrl: "../icons/254.png",
title: $(elm).find("title").text(),
message: "(" + msToTime((Date.now() - new Date($(elm).find("pubDate").text()).getTime())) + ") ",
requireInteraction: true
},
function() {}
);
}
}.bind(this));
console.log("Max date = " + Math.max.apply(null, pubDates))
existingFeedData.lastUpdate = Math.max.apply(null, pubDates);
Storage.saveFeedData(existingFeedData);
},
getUrlForFeed: function($feed, feedSettings) {
if (feedSettings.linkType && $feed.find(feedSettings.linkType).length > 0) {
return $feed.find(feedSettings.linkType).text();
} else if ($feed.find('link').length == 1) {
return $feed.find('link').text();
} else {
if ($feed.find('link[rel="shorturl"]').length > 0) {
return $feed.find('link[rel="shorturl"]').attr('href');
} else if ($feed.find('link[rel="alternate"]').length > 0) {
return $feed.find('link[rel="alternate"]').attr('href');
} else if ($feed.find('link[rel="related"]').length > 0) {
return $feed.find('link[rel="related"]').attr('href');
} else {
return $feed.find('link').first();
}
}
},
saveFeed: function(existingFeedData, newFeedData) {
;
},
updateReadStatusOfNewItems: function(oldItems, newItems) {
if (typeof oldItems === 'undefined' || Object.keys(oldItems).length == 0 || Object.keys(newItems).length == 0) {
return newItems;
}
Object.keys(newItems).forEach(function(url) {
if (oldItems[url]) {
newItems[url].unread = oldItems[url].unread;
}
});
return newItems;
},
setNewBadge: function() {
chrome.browserAction.setBadgeText({
text: 'NEW'
});
}
};
}());
chrome.alarms.onAlarm.addListener(function(alarm) {
if (alarm.name == 'updateRssFeeds') {
console.log("Called!")
updateRssFeeds();
}
});
chrome.notifications.onClicked.addListener(function(notificationId) {
chrome.tabs.create({
url: notificationId
});
});
chrome.runtime.onInstalled.addListener(onInstall);
function onInstall() {
chrome.runtime.openOptionsPage();
chrome.alarms.create('updateRssFeeds', {
when: 0,
periodInMinutes: 1
});
}
function updateRssFeeds() {
Storage.getFeeds(function(feeds) {
feeds.forEach(function(feed) {
FeedService.downloadFeed(feed);
});
}, true, false)
}
This is my current code:
function leaderboardembed() {
const filtered = client.points.filter(p => p.guild === message.guild.id).array();
let orilent;
const sorted = filtered.sort((a, b) => b.vouches - a.vouches );
let embeds = [];
let j = 0;
let first = (10)
let maxnum = 50;
orilent = sorted.length;
if(isNaN(maxnum)) {
console.log("maximum_leaderboard NOT A NUMBER")
maxnum = 50;}
if (maxnum > sorted.length)
maxnum = sorted.length + (10 - Number(String(sorted.length/10).slice(2)));
if (maxnum < 10) maxnum = 10;
for (let i = 10; i <= maxnum; i += 10) {
const top = sorted.splice(0, 10);
const embed = new Discord.MessageEmbed()
.setTitle(`\`${message.guild.name}\` | Leaderboard`)
.setTimestamp()
.setDescription(`Top ${i<orilent?i:orilent}/${orilent} Ranking:`)
.setColor(embedcolor);
for (const data of top) {
j++;
try {
embed.addField(`**${j}**. \`${data.usertag}\``, ` | **Vouches:** \`${data.vouch}\``);
} catch {
embed.addField(`**${j}**. \`${data.usertag}\``, ` | **Vouches:** \`${data.vouch}\``);
}
}
embeds.push(embed);
}
return embeds;
}
async function leaderboard() {
let currentPage = 0;
const embeds = leaderboardembed();
if (embeds.length == 1)
return message.channel.send(embeds[0]).catch(e=>console.log("ranking: " + e))
const lbembed = await message.channel.send(
`**Current Page - ${currentPage + 1}/${embeds.length}**`,
embeds[currentPage]).catch(e=>console.log("ranking: " + e));
try {
await lbembed.react("⏪");
await lbembed.react("⏹");
await lbembed.react("⏩");
} catch (error) {
console.error(error);
}
const filter = (reaction, user) => ["⏪", "⏹", "⏩"].includes(reaction.emoji.name) && message.author.id === user.id;
const collector = lbembed.createReactionCollector(filter, {
time: 60000
});
collector.on("collect", async (reaction, user) => {
try {
if (reaction.emoji.name === "⏩") {
if (currentPage < embeds.length - 1) {
currentPage++;
lbembed.edit(`**Current Page - ${currentPage + 1}/${embeds.length}**`, embeds[currentPage]);
}
} else if (reaction.emoji.name === "⏪") {
if (currentPage !== 0) {
--currentPage;
lbembed.edit(`**Current Page - ${currentPage + 1}/${embeds.length}**`, embeds[currentPage]);
}
} else {
collector.stop();
reaction.message.reactions.removeAll();
}
await reaction.users.remove(message.author.id);
} catch (error) {
console.error(error);
}
});
}
This is how it displays when doing the command
The problem is that there's about 1000 members and some don't have the "vouches" and id rather just display the people that have the highest to lowest.
Here's an example of what I'm aiming it to be like:
Firstly, to sort all of them, you do need them all in an array. Otherwise, it will be excessively difficult. Here is a simple sort function that JavaScript provides for us:
var ranks = [7, 2, 9, 5, 10, 110]
ranks.sort() //don’t use this, this would put 10, 110, 2 etc since it’s alphabetical, you need to specify how to sort it
ranks.sort((a, b)=> a - b) //2, 5, 7, 9, 10, 110
//to reverse that
ranks.sort((a, b) => b-a)
I am using node-querybuilder module for executing queries. I need to loop query in loop.
dbConfig.getDB() returns connection and why loop is not waiting for the query to execute
firstHalf and secondHalf always A and A..loop is not waiting for query to be executed.
I am very new to node-querybuilder module.using async and await doesn't make any difference
dbConfig.getDB().query(`SELECT id, full_name, email,mobile from frms WHERE status = ? AND email IS NOT NULL`, ['1'], async (err, response) => {
if (err) {
console.log(err);
}
else if (response && response.length > 0) {
var curr = new Date;
var firstday = new Date(curr.setDate(curr.getDate() - curr.getDay() - 6));
var weekArr = [firstday];
for (let j = 1; j < 6; j++) {
var nextday = new Date(curr.setDate(firstday.getDate() + j));
weekArr.push(nextday);
}
// console.log(weekArr);
let dayWiseAttendanceArr = [];
for (let i = 0; i < response.length; i++) {
let frmWiseObj = {};
frmWiseObj.basic_details = { "id": response[i].id, "name": response[i].full_name, "email": response[i].email, "mobile": response[i].mobile };
frmWiseObj.attendance_details = [];
for (let k = 0; k < weekArr.length; k++) {
let inDate = weekArr[k].toISOString().split('T')[0];
let dateWiseObj = {};
dateWiseObj.date = inDate;
let firstHalf = "A";
let secondHalf = "A";
let query = `SELECT DATE_FORMAT(in_date,'%Y-%m-%d') as date,ifnull(in_date,'') as punch_in,ifnull(out_date,'') as punch_out FROM punching_detail where DATE_FORMAT(in_date,'%Y-%m-%d') = '${inDate}' and punching_detail.frm_id='${response[i].id}'`;
dbConfig.getDB().query(query, async (punch_err, punch_response) => {
if (punch_err) {
console.log(punch_err);
} else if (punch_response && punch_response.length > 0) {
let punch_in = punch_response[0].punch_in;
let punch_out = punch_response[0].punch_out;
if (punch_in <= (inDate + " 10:40:00") && punch_out >= (inDate + " 19:00:00")) {
firstHalf = "P";
secondHalf = "P";
} else if (punch_in <= (inDate + " 10:40:00") && punch_out <= (inDate + " 19:00:00") && punch_out >= (inDate + " 14:30:00")) {
firstHalf = "P";
secondHalf = "A";
} else if (punch_in >= (inDate + " 10:40:00") && punch_in <= (inDate + " 14:30:00") && punch_out >= (inDate + " 19:00:00")) {
firstHalf = "A";
secondHalf = "P";
} else if (punch_in >= (inDate + " 10:40:00") && punch_in <= (inDate + " 14:30:00") && punch_in <= (inDate + " 19:00:00")) {
var hours = Math.abs(punch_out - punch_in) / (60 * 60 * 1000);
if (hours >= 4.5) {
firstHalf = "P";
secondHalf = "A";
} else {
firstHalf = "P";
secondHalf = "P";
}
} else {
firstHalf = "A";
secondHalf = "A";
}
} else {
let query = `SELECT leave_type,start_day, end_day,DATE_FORMAT(start_date,'%Y-%m-%d') as date,DATE_FORMAT(end_date,'%Y-%m-%d') as end_date FROM leave_mgt where (DATE_FORMAT(end_date,'%Y-%m-%d') <= '${inDate}}' AND DATE_FORMAT(start_date,'%Y-%m-%d') <= '${inDate}}') and frm_id='${response[i].id}' and status = '1'`;
await dbConfig.getDB().query(query, async (leave_err, leave_response) => {
if (leave_err) {
console.log(leave_err);
} else if (leave_response && leave_response.length > 0) {
const element = leave_response[0];
if (element.end_date != '0000-00-00') {
if (element.start_date == inDate) {
if (element.start_day == 2) {
firstHalf = 'L';
secondHalf = 'L';
} else if (element.start_day == 0) {
firstHalf = 'L';
} else if (element.start_day == 1) {
secondHalf = 'L';
}
} else if (element.end_date == inDate) {
if (element.end_day == 2) {
firstHalf = 'L';
secondHalf = 'L';
} else if (element.end_day == 0) {
firstHalf = 'L';
} else if (element.end_day == 1) {
secondHalf = 'L';
}
} else {
firstHalf = 'L';
secondHalf = 'L';
}
}
}
});
}
dateWiseObj.first_half = firstHalf;
dateWiseObj.second_half = secondHalf;
frmWiseObj.attendance_details.push(dateWiseObj);
});
}
dayWiseAttendanceArr.push(frmWiseObj);
}
}
else {
console.log("No Active frms found");
}
});
}```
Am not familiar with node-querybuilder but on this line:
await dbConfig.getDB().query(query, async (leave_err, leave_response) => {/* some code */ });
since the query method takes in a callback, it probably does not return a promise (you could confirm this by looking at the documentation/source code). This means using await here will not make your code wait for query to complete (since await has to be followed by a promise for it to have any effect).
If the library you're using doesn't use promises, maybe try promisifying the query method, so that you can await it. Promisifying can be done using something built-in like util/promisify or manually by creating a wrapper function (something like below):
function queryAsPromise(sql, parameters) {
return new Promise((resolve, reject) => {
dbConfig.getDB().query(sql, parameters, (err, response) => {
if (err) {
return reject(err);
}
resolve(response);
});
});
}
You can then await whatever queryAsPromise resolves to, like below:
async function main() {
const response = await queryAsPromise(
`SELECT id, full_name, email,mobile from frms WHERE status = ? AND email IS NOT NULL`,
[1]
);
// Rest of your code...
}
Why don't you just use a promisified helper function to execute your queries and return the result?
// DB setup
const dbConfig = {
host: process.env.DATABASE_HOST,
user: process.env.DATABASE_USER,
password: process.env.DATABASE_PASS,
database: process.env.DATABASE_NAME,
port: process.env.DATABASE_PORT,
}
// Connection handler
const handleConnection = () => {
return new Promise((resolve, reject) => {
const client = mysql.createConnection(dbConfig); // Recreate the connection, since the old one cannot be reused.
client.connect(function onConnect(err) { // The server is either down
if (err) { // or restarting (takes a while sometimes).
console.log('error when connecting to db:', err);
setTimeout(handleConnection, 10000); // We introduce a delay before attempting to reconnect,
return;
} // to avoid a hot loop, and to allow our node script to
console.log('Database connected.');
resolve(client);
});
// process asynchronous requests in the meantime.
// If you're also serving http, display a 503 error.
client.on('error', function onError(err) {
console.log('db error', err);
if (err.code == 'PROTOCOL_CONNECTION_LOST') { // Connection to the MySQL server is usually
handleConnection(); // lost due to either server restart, or a
} else { // connnection idle timeout (the wait_timeout
reject(err); // server variable configures this)
}
});
})
}
// Query handler
export.mySql = (query, values = null) => {
return new Promise(async (resolve, reject) => {
try {
const client = await handleConnection();
let db = client;
db.query(query, values, (err, result, fields) => {
if (!err) {
resolve(result);
client.end();
console.log('Query executed');
return;
}
client.end();
throw err;
});
} catch (error) {
reject(err);
}
});
}
So whenever you call the mySql function; it connects to DB, executes the query, and returns the result.
Usage:
const { mySql } = require('./db.js');
const result = await mySql(`SELECT id, full_name, email,mobile from frms WHERE status = ? AND email IS NOT NULL`, ['1']);
console.log(result);
I have a NodeJS+Express REST API method executing reverse geocoding (using Google's Maps API).
I'm trying to solve it with Promises but the 'then' is getting executed before my function returns with the answers from Google.
When testing the same code just calling a setTimeout, it works as expected. Please see comments in the code (simplify version).
app.get('/api/v1/events', verifyToken, async (req, res) => {
await db.poolPromise.then(pool => {
return pool.request()
.input('UserId', db.sql.UniqueIdentifier, res.authData.userId)
.input('DateFrom', db.sql.DateTime2(7), req.query.dateFrom)
.input('DateTill', db.sql.DateTime2(7), req.query.dateTo)
.output('UserIdAuthorized', db.sql.Bit)
.execute('sp')
}).then(result => {
let output = (result.output || {})
if (!output.UserIdAuthorized) {
res.sendStatus(403)
}
else if (result.recordset.length > 0) {
(new Promise( (resolve) => {
//resolve(123) // this one works as expected
//setTimeout(resolve, 3000, 'temp success') // this one works as expected
// *** this one get passed and the following then is being executed before it answers ***
resolve( getAddress_TEST(result.recordset) )
// **************************************************************************************
})).then(function (value) {
res.json(
{
meta: { count: 10 }, //this is just a sample
result: value // *** this one fails with undefined ***
})
})
} else {
res.sendStatus(404)
}
}).catch(err => {
res.sendStatus(500)
console.error(err)
})
});
const nodeGeocoder_options = {
provider: 'google',
apiKey: process.env.GOOGLE_API_KEY
}
async function getAddress_TEST(recordset) {
//sample recordset for debugging - as you dont have my database
recordset = [{'eventId':14205556,'Lat':54.57767,'Lon':-2.4920483},{'eventId':14205558,'Lat':54.57767,'Lon':-2.492048},{'eventId':14205579,'Lat':53.416908,'Lon':-2.952071},{'eventId':14205588,'Lat':52.644448,'Lon':-1.153185},{'eventId':14205601,'Lat':52.29174,'Lon':-1.532283},{'eventId':14205645,'Lat':52.644448,'Lon':-1.153185},{'eventId':14205801,'Lat':53.68687,'Lon':-1.498708},{'eventId':14206041,'Lat':51.471521,'Lon':-0.2038033},{'eventId':14206049,'Lat':51.471521,'Lon':-0.2038033},{'eventId':14206072,'Lat':51.471521,'Lon':-0.2038033}]
let geocoder = nodeGeocoder(nodeGeocoder_options)
let ps = []
for (var i = 0, length = recordset.length; i < length; i++) {
if (i == 0 || !(i > 0
&& recordset[i - 1].Lat == recordset[i].Lat
&& recordset[i - 1].Lon == recordset[i].Lon)) {
ps.push(new Promise(function (resolve) {
resolve(reverseGeocode(geocoder, recordset[i].Lat, recordset[i].Lon))
}))
} else {
ps.push('-')
}
}
await Promise.all(ps)
.then(function (values) {
for (var i = 0, length = values.length; i < length; i++) {
if (values[i] != '-') {
recordset[i].locationAddress = values[i]
} else {
recordset[i].locationAddress = recordset[i - 1].locationAddress
}
}
}).then(function () {
recordset.forEach(function (v) {
delete v.Lat
delete v.Lon
});
console.log(recordset)
return recordset
})
};
async function reverseGeocode(geocoder, lat, lon) {
let address = '+'
if (lat != 0 && lon != 0) {
await geocoder.reverse({ lat: lat, lon: lon })
.then(res => {
address = res[0].formattedAddress
})
.catch(err => {
console.error(err)
});
}
return address
};
I'm sure it is something simple that I'm missing here...
The basic problem is that your getAddress_TEST function returns a promise that fulfills with nothing (undefined), because it does not contain a return statement. The return recordset is in a then() callback, from where it affects the promise resolution of the awaited promise, but that result is thrown away.
If you want to use async/await, you should get rid of any new Promise and then calls:
app.get('/api/v1/events', verifyToken, async (req, res) => {
try {
const pool = await db.poolPromise
const result = await pool.request()
.input('UserId', db.sql.UniqueIdentifier, res.authData.userId)
.input('DateFrom', db.sql.DateTime2(7), req.query.dateFrom)
.input('DateTill', db.sql.DateTime2(7), req.query.dateTo)
.output('UserIdAuthorized', db.sql.Bit)
.execute('sp')
let output = (result.output || {})
if (!output.UserIdAuthorized) {
res.sendStatus(403)
} else if (result.recordset.length > 0) {
const value = await getAddress_TEST(result.recordset)
res.json({
meta: { count: 10 }, //this is just a sample
result: value
})
} else {
res.sendStatus(404)
}
} catch(err) {
res.sendStatus(500)
console.error(err)
}
});
const nodeGeocoder_options = {
provider: 'google',
apiKey: process.env.GOOGLE_API_KEY
}
async function getAddress_TEST(recordset) {
const geocoder = nodeGeocoder(nodeGeocoder_options)
const ps = recordset.map((record, i) => {
if (i == 0 || !(i > 0
&& recordset[i - 1].Lat == record.Lat
&& recordset[i - 1].Lon == recordLon)) {
return reverseGeocode(geocoder, recordset[i].Lat, recordset[i].Lon))
} else {
return '-'
}
});
const values = await Promise.all(ps)
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
for (var i = 0, length = values.length; i < length; i++) {
if (values[i] != '-') {
recordset[i].locationAddress = values[i]
} else {
recordset[i].locationAddress = recordset[i - 1].locationAddress
}
}
recordset.forEach(function (v) {
delete v.Lat
delete v.Lon
});
console.log(recordset)
return recordset
// ^^^^^^^^^^^^^^^^
}
async function reverseGeocode(geocoder, lat, lon) {
if (lat != 0 && lon != 0) {
const res = await geocoder.reverse({ lat: lat, lon: lon })
return res[0].formattedAddress
}
return '+'
}
I am trying to export rallygrid data in excel file, but getting only headers not values.
Below is my code which I wrote to generate grid and export button
From here [https://github.com/andreano/TaskDelta/blob/master/App.js], I stole the export code
prepareChart: function(iteration_data) {
this.converted_values = [];
this.accept_values = [];
this.commit_values = [];
parents = [];
rootParent = this.getContext().getProject().Name;
sortedArray = [];
var project_hash = {}; // project_by_name, with children
Ext.Array.each(iteration_data, function(iteration){
if ((iteration.ProjectName != rootParent && iteration.ChildCount > 0) || iteration.ParentName == rootParent) {
parents.push(iteration.ProjectName);
}
// make a place for me
if ( ! project_hash[iteration.ProjectName] ) { project_hash[iteration.ProjectName] = []; }
// make a place for my parent so it can know children
if ( iteration.ParentName ) {
if ( ! project_hash[iteration.ParentName]) { project_hash[iteration.ParentName] = []; }
project_hash[iteration.ParentName] = Ext.Array.merge( project_hash[iteration.ParentName], iteration.ProjectName);
}
}, this);
// build order this way:
//console.log("Current: ", this.getContext().getProject().Name );
// order the array by parents to children to grandchildren
sortedArray = this._getTreeArray( this.getContext().getProject().Name , project_hash);
parents = Ext.Array.unique(parents);
sortedData = [];
Ext.Array.each(sortedArray, function(name){
Ext.Array.each(iteration_data, function(ite){
if(ite.ProjectName == name) {
sortedData.push(ite);
};
});
});
Ext.Array.each(iteration_data, function(iteration){
if (iteration.ProjectName == rootParent) {
sortedData.push(iteration);
}
}, this);
iteration_data = sortedData;
sprints = [];
teams = [];
this.ratio = {};
for ( var i=0; i<iteration_data.length; i++ ) {
commit_accept_ratio = 0;
var data_point = iteration_data[i];
this.commit_values.push( data_point.Commit );
this.accept_values.push( data_point.Accept );
if ( data_point.Commit > data_point.Accept ) {
this.converted_values.push( data_point.Commit - data_point.Accept );
} else {
this.converted_values.push( 0 );
}
if (data_point.Commit != 0) {
commit_accept_ratio = (data_point.Accept / data_point.Commit ) * 100;
} else {
commit_accept_ratio = 0;
};
sprints.push(iteration_data[i].Name);
teams.push(iteration_data[i].ProjectName);
teams.push(rootParent);
this.ratio[data_point.ObjectID] = commit_accept_ratio;
}
this.sprints = Ext.Array.unique(sprints).sort();
this.teams = Ext.Array.unique(teams);
removable_teams = [];
for ( var i=0; i<this.teams.length; i++ ) {
team_name = null;
var count = 0;
Ext.Array.each(iteration_data, function(data) {
if (this.teams[i] == data.ProjectName && data.Commit == 0 || null || undefined && data.Accept == 0 || null || undefined) {
count += 1;
team_name = data.ProjectName;
}
}, this);
if (count == this.sprints.length) {
removable_teams.push(team_name);
}
}
removable_teams = Ext.Array.unique(removable_teams);
records = [];
recordHash = {};
summaryHash = {};
Ext.Array.each(iteration_data, function(iter) {
if (!recordHash[iter.ProjectName]) {
recordHash[iter.ProjectName] = {
Team: iter.ProjectName,
Name: '4 Sprint Summary',
Commit: [],
Accept: [],
Perc: [],
Summary: 0
};
}
if (!Ext.Array.contains(removable_teams, iter.ProjectName)) {
recordHash[iter.ProjectName]["Commit-" + iter.Name] = iter.Commit;
recordHash[iter.ProjectName]["Accept-" + iter.Name] = iter.Accept;
recordHash[iter.ProjectName]["Perc-" + iter.Name] = this.ratio[iter.ObjectID];
}
}, this);
var summaryArray = Ext.Array.slice( this.sprints, (this.sprints.length - 4))
var iterated_data = [];
Ext.Array.each(summaryArray, function(summ){
Ext.Array.each(iteration_data, function(team) {
if( summ == team.Name){
iterated_data.push(team);
}
});
});
Ext.Array.each(iteration_data, function(summ){
Ext.Array.each(iterated_data, function(team) {
if (!summaryHash[team.ProjectName]) {
summaryHash[team.ProjectName] = {
Commit: 0,
Accept: 0,
Total: 0
};
};
if (!Ext.Array.contains(removable_teams, team.ProjectName)) {
if( summ.ProjectName == team.ProjectName && summ.Name == team.Name) {
summaryHash[team.ProjectName]["Commit"] += summ.Commit;
summaryHash[team.ProjectName]["Accept"] += summ.Accept;
if (summaryHash[team.ProjectName]["Commit"] != 0) {
summaryHash[team.ProjectName]["Total"] = (summaryHash[team.ProjectName]["Accept"] / summaryHash[team.ProjectName]["Commit"] ) * 100;
} else {
summaryHash[team.ProjectName]["Total"] = 0;
};
};
}
});
}, this);
Ext.Object.each(recordHash, function(key, value) {
if (summaryHash[key]) {
value["Summary"] = summaryHash[key].Total;
records.push(value);
}
});
var cfgsValues = [];
cfgsValues.push({text: 'Team', style:"background-color: #D2EBC8", dataIndex: 'Team', width: 170, renderer: function(value, meta_data, record, row, col) {
if (Ext.Array.contains(parents, value)) {
meta_data.style = "background-color: #FFF09E";
return Ext.String.format("<div style='font-weight:bold;text-align:center'>{0}</div>", value);
} else if (rootParent == value){
meta_data.style = "background-color: #CC6699";
return Ext.String.format("<div style='font-weight:bold;text-align:center'>{0}</div>", value);
} else {
return value;
};
}});
cfgsValues.push({text: '4 Sprint Summary', style:"background-color: #D2EBC8", width: 70, dataIndex: 'Summary', renderer: function(value, meta_data, record) {
var color = null;
if (value >= 80 && value <= 120) {
color = "#00AF4F";
}
else if (value >= 60 && value <= 80) {
color = "#FBFE08";
}
else if (value <= 60) {
color = "#FC0002";
}
else if (value >= 120) {
color = "#98CCFB";
};
meta_data.style = "background-color: "+color+"";
return Ext.Number.toFixed(value, 0)+"%";
}});
Ext.Array.each(this.sprints, function(sprint) {
cfgsValues.push(
{text: sprint, style:'background-color:#D2EBC8;text-align:center;font-weight:bold', defaults: {enableColumnHide:false}, columns:[
{text: "Commit", dataIndex: 'Commit-' + sprint, width: 50, renderer: function(value, meta_data, record) {
if( value ) {
return value;
} else {
return "NA";
}
}},
{text: "Accept", dataIndex: 'Accept-' + sprint, width: 60, renderer: function(value, meta_data, record) {
if( value) {
return value;
} else {
return "NA";
}
}},
{text: "%", dataIndex: 'Perc-'+ sprint, width: 50, renderer: function(value, meta_data, record) {
var color = null;
if (value >= 80 && value <= 120) {
color = "#00AF4F";
}
else if (value >= 60 && value <= 80) {
color = "#FBFE08";
}
else if (value <= 60) {
color = "#FC0002";
}
else if (value >= 120) {
color = "#98CCFB";
}
meta_data.style = "background-color: "+color+"";
if (value) {
return Ext.Number.toFixed(value, 0)+"%";
} else {
return "NA";
};
}}
]}
);
});
var chart = Ext.getCmp('mychart');
if (chart) {
chart.destroy();
};
Ext.Array.each(this.sprints, function(sprint) {
Ext.Array.each(records, function(record) {
if (record["Accept-" + sprint] == undefined) {
record["Accept-" + sprint] = undefined;
}
if (record["Commit-" + sprint] == undefined) {
record["Commit-" + sprint] = undefined;
}
if (record["Perc-" + sprint] == undefined) {
record["Perc-" + sprint] = undefined;
}
});
});
this.add({
xtype: 'rallygrid',
id: 'mychart',
store: Ext.create('Rally.data.custom.Store', {
data: records,
pageSize: 100
}),
//viewConfig: {
//stripeRows: false
//},
columnCfgs: cfgsValues,
//columnLines: true
});
this.globalStore = Ext.getCmp('mychart');
console.log("this.globalStore", this.globalStore);
this.down('#grid_box').add(this.globalStore);
//this.setLoading(false);
},
_addPrintButton: function() {
var me = this;
this.down('#print_button_box').add( {
xtype: 'rallybutton',
itemId: 'print_button',
text: 'Export to Excel',
disabled: false,
margin: '20 10 10 0',
region: "right",
handler: function() {
me._onClickExport();
}
});
},
_onClickExport: function () { //using this function to export to csv
var that = this;
if (this.down('#grid_box')){
//Ext.getBody().mask('Exporting Tasks...');
//console.log('inside export');
setTimeout(function () {
var template = '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-' +
'microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"><head>' +
'<!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>' +
'{worksheet}</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet>' +
'</x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--></head><body><table>{table}' +
'</table></body></html>';
var base64 = function (s) {
return window.btoa(unescape(encodeURIComponent(s)));
};
var format = function (s, c) {
return s.replace(/{(\w+)}/g, function (m, p) {
return c[p];
});
};
var table = that.getComponent('grid_box');
//console.log("Exporting table ",table);
var excel_data = '<tr>';
Ext.Array.each(table.getEl().dom.outerHTML.match(/<span .*?x-column-header-text.*?>.*?<\/span>/gm), function (column_header_span) {
excel_data += (column_header_span.replace(/span/g, 'td'));
});
excel_data += '</tr>';
Ext.Array.each(table.getEl().dom.outerHTML.match(/<tr class="x-grid-row.*?<\/tr>/gm), function (line) {
excel_data += line.replace(/[^\011\012\015\040-\177]/g, '>>');
});
//console.log("Excel data ",excel_data);
var ctx = {worksheet: name || 'Worksheet', table: excel_data};
window.location.href = 'data:application/vnd.ms-excel;base64,' + base64(format(template, ctx));
Ext.getBody().unmask();
}, 500);
}else{
console.log("grid_box does not exist");
}
}
There is an example in new AppSDK2 documentation of exporting to CSV.
I also have an example of exporting to CSV in this github repo.