I'm trying to create a simple interactive CLI with Node.js, that collects input from the user by "talking" to them asking questions.
I'm working with the 'readline' module that forces me to use the callback mechanism, currently results in the weird cascade below.
As I'm new to Node.js and to the callback idea, I really feel I'm doing something wrong but not sure how to make it better.
How can I reformat my function so it will be more elegant?
(I know the function below won't return anything, it's just for my temp debugging)
function getUserInput(dbData) {
readline.question('What would you like to edit?\n\n\t(1) Cars\t(2) Data owners\n', choice => {
if (choice == 1) {
readline.question('Choose operation:\n\n\t(1) Add new\n', op => {
if (op == 1) {
let newCar = {};
console.log("--> Please fill in the required details (with Capital First Letter):\n\n");
readline.question("Car name: ", carName => {
newCar.name = carName;
readline.question("Car make: ", carMake => {
newCar.make = carMake;
readline.question("Team (DC/CSF): ", team => {
newCar.team = team;
readline.question("TC (1/2): ", tc => {
newCar.tc = tc;
readline.close();
console.log(newCar);
});
});
});
});
}
else {
console.log("Invalid choice!\n");
getUserInput(dbData);
}
});
}
else {
console.log("Invalid choice!\n");
getUserInput(dbData);
};
});
Related
I need some advice on how to structure this function as at the moment it is not happening in the correct order due to node being asynchronous.
This is the flow I want to achieve; I don't need help with the code itself but with the order to achieve the end results and any suggestions on how to make it efficient
Node routes a GET request to my controller.
Controller reads a .csv file on local system and opens a read stream using fs module
Then use csv-parse module to convert that to an array line by line (many 100,000's of lines)
Start a try/catch block
With the current row from the csv, take a value and try to find it in a MongoDB
If found, take the ID and store the line from the CSV and this id as a foreign ID in a separate database
If not found, create an entry into the DB and take the new ID and then do 6.
Print out to terminal the row number being worked on (ideally at some point I would like to be able to send this value to the page and have it update like a progress bar as the rows are completed)
Here is a small part of the code structure that I am currently using;
const fs = require('fs');
const parse = require('csv-parse');
function addDataOne(req, id) {
const modelOneInstance = new InstanceOne({ ...code });
const resultOne = modelOneInstance.save();
return resultOne;
}
function addDataTwo(req, id) {
const modelTwoInstance = new InstanceTwo({ ...code });
const resultTwo = modelTwoInstance.save();
return resultTwo;
}
exports.add_data = (req, res) => {
const fileSys = 'public/data/';
const parsedData = [];
let i = 0;
fs.createReadStream(`${fileSys}${req.query.file}`)
.pipe(parse({}))
.on('data', (dataRow) => {
let RowObj = {
one: dataRow[0],
two: dataRow[1],
three: dataRow[2],
etc,
etc
};
try {
ModelOne.find(
{ propertyone: RowObj.one, propertytwo: RowObj.two },
'_id, foreign_id'
).exec((err, searchProp) => {
if (err) {
console.log(err);
} else {
if (searchProp.length > 1) {
console.log('too many returned from find function');
}
if (searchProp.length === 1) {
addDataOne(RowObj, searchProp[0]).then((result) => {
searchProp[0].foreign_id.push(result._id);
searchProp[0].save();
});
}
if (searchProp.length === 0) {
let resultAddProp = null;
addDataTwo(RowObj).then((result) => {
resultAddProp = result;
addDataOne(req, resultAddProp._id).then((result) => {
resultAddProp.foreign_id.push(result._id);
resultAddProp.save();
});
});
}
}
});
} catch (error) {
console.log(error);
}
i++;
let iString = i.toString();
process.stdout.clearLine();
process.stdout.cursorTo(0);
process.stdout.write(iString);
})
.on('end', () => {
res.send('added');
});
};
I have tried to make the functions use async/await but it seems to conflict with the fs.openReadStream or csv parse functionality, probably due to my inexperience and lack of correct use of code...
I appreciate that this is a long question about the fundamentals of the code but just some tips/advice/pointers on how to get this going would be appreciated. I had it working when the data was sent one at a time via a post request from postman but can't implement the next stage which is to read from the csv file which contains many records
First of all you can make the following checks into one query:
if (searchProp.length === 1) {
if (searchProp.length === 0) {
Use upsert option in mongodb findOneAndUpdate query to update or upsert.
Secondly don't do this in main thread. Use a queue mechanism it will be much more efficient.
Queue which I personally use is Bull Queue.
https://github.com/OptimalBits/bull#basic-usage
This also provides the functionality you need of showing progress.
Also regarding using Async Await with ReadStream, a lot of example can be found on net such as : https://humanwhocodes.com/snippets/2019/05/nodejs-read-stream-promise/
im searching for an idea to fix my problem. First of all, there is a server.exe software, that can load some stuff. But if i change something, it needs a restart, but not, if i use a json file to store account names. Look:
const allowedPlayers =
[
"Socialclubuser1",
"Socialclubuser2",
"Socialclubuser3"
]
mp.events.add("playerJoin", (player) => {
if (!allowedPlayers.includes(player.socialClub)) {
player.notify('Youre not whitelisted!');
}
});
mp.events.add("playerJoin", (player) => {
if (!allowedPlayers.includes(player.socialClub)) {
player.kick('Youre not whitelisted!');
}
});
i would use account.json, and insert there the stuff but how?
greetings
Create an account.json file and require it using require on start.
// account.json
// ["Socialclubuser1", "Socialclubuser2", "Socialclubuser3"]
const allowedPlayers = require("./account.json");
// rest of the code
mp.events.add("playerJoin", player => {
if (allowedPlayers.indexOf(player.socialClub) === -1) {
player.notify("Youre not whitelisted!");
player.kick("Youre not whitelisted!");
}
});
I tried to find some answer for this, but got nothing relative. I need to implement something like form as row of Q&A, for example:
U: /form
B: Enter your name
U: John
B: Hello, John. Are you 12 years old?
...
and so on. And I'm going to write answers in Google Spreadsheets, but there's no problem, I just don't know how to create a chain on phrases with bot
Thanks for attention
Okay so the basis of my answer is on a Node library I use for making telegram bots in all my projects. It's called telegram-node-bot, the way other libraries would do it will differ but I like this method cause it lifts all of the heavy load for me and easy to understand.
Here we go:
const Telegram = require('telegram-node-bot');
const TelegramBaseController = Telegram.TelegramBaseController;
const TextCommand = Telegram.TextCommand;
const tg = new Telegram.Telegram('YOUR_TOKEN'); //Ask botfather for your token
class FormController extends TelegramBaseController {
/**
* #param {Scope} $
*/
formHandler($) {
const form = {
name: {
q: 'Send me your name',
error: 'Sorry, wrong input',
validator: (message, callback) => {
const answer = message.text;
if (answer) {
callback(true, answer);
return
}
callback(false);
}
},
age: {
q: 'Send me your age',
error: 'sorry, wrong input',
validator: (message, callback) => {
const answer = message.text;
if (answer && IsNumeric(answer)) {
callback(true, toInt(answer))
return
}
callback(false)
}
}
}
$.runForm(form, (result) => {
console.log(result)
})
}
get routes() {
return {
'formCommand': 'formHandler'
}
}
}
tg.router
.when(
new TextCommand('/form', 'formCommand'),
new FormController()
)
The logic/algorithm behind it is simple:
Ask a question.
Wait for a response.
Check if the response fits the criterias you've set for the right answer format
If false ask the question again.
If true move to the next question.
For testing I am trying to stop all commands, unless in a certain channel. I know how to do this for each command specifically, but I am trying to catch it inside of the main bot file, and return a message. I have tried two ways so far:
bot.on('command', async m => { (Also tried 'commandmessage')
console.log('COMMAND');
if (m.channel != 'bot-testing') {
return m.channel.send('You can\'t use commands here!');
}
});
Which doesn't work at all. Then I tried this:
bot.on('message', async m => {
m.isDM = (m.guild ? false : true);
if (m.content[0] != bot.commandPrefix) {
return;
} else {
if (m.channel != 'bot-testing') {
m.channel.send('You can\'t use commands here!');
}
}
});
Which kind of works, but doesn't stop the command.
It looks like you were super close - you just need to look at m.channel.name in your second if-statement (using method #2):
bot.on('message', async m => {
// ...
if (m.content[0] != bot.commandPrefix) {
return;
} else {
// [NEW: add .name prop here]
if (m.channel.name != 'bot-testing') {
m.channel.send('You can\'t use commands here!');
}
}
});
I'm developing Ionic2 App which is running on android phone.
It does not need remote db. so I decided to use local storage 'Store'.
I found some article and made some improvements like below.
I succeded to get all values but I want to get something on some condition like 'where' statement in SQL. (getWorksOn(someDate){...})
I searched and read more than 30 top articles but I couldn't find the way.
Please let me know if there's any way.
// work-service.ts
addWork(work){
this.storage.get('myWorks').then((data) => {
if(data != null){
data.push(work);
this.storage.set('myWorks', data);
} else {
let tempWorks = [];
tempWorks.push(work);
this.storage.set('myWorks', tempWorks);
}
});
}
getWorks(){
return this.storage.get('myWorks');
}
getWorksOn(someDate){
// I want to return works that worked on someDate
// Is there any way to get some value conditionally
return this.storage.get('myWorks');
}
// work-list.ts
works:any;
...
ngOnInit(){
this.workService.getWorks().then((data)=>{
this.works = data;
});
}
If you're using Lodash then you can do it as shown below.
Note: You cannot do this inside the local storage.You have to do something like this then...
getWorksOn(someDate){
this.storage.get('myWorks').then((val) => {
let myworkArray = val; //firstly, fetched the data
let myworkArray2= _.filter(myworkArray , { 'date': someDate});//after that filter out according to your condition
});
}
OP's Feedbackusing map and filter function of array
this.workService.getWorks().then((works) => { this.works = works.filter(work
=> work.dateStr === this.dateStr ); });