Reading first lines from textfile without loading whole textfile into memory - node.js

I have a large amount of small textfiles where the first 4 or less lines contain metadata; following is an example
Lorem Ipsum
Tag1 Tag2 Tag3
Text
4204
‎‎‎‎‎‎
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Lorem Ipsum; would be the title
Tag1, Tag2, and Tag3; would be an array of tags
Text; would be the type
4204; would be the ID.
Lorem ipsum [...]; would be the actual content
I need to load the metadata without loading the actual content. Im working in node.js. I made following code:
function readMeta (path, callback) {
const meta = {};
const lineReader = require("readline").createInterface({input: require("fs").createReadStream(path)});
let lineCount = 0;
let interpretedMeta;
lineReader.on("line", line => {
interpretedMeta = interpretMeta(line, lineCount)
switch (lineCount) {
case 0:
meta.name = interpretedMeta;
break;
case 1:
meta.tags = interpretedMeta.split(" ");
break;
case 2:
meta.type = interpretedMeta;
break;
case 3:
meta.id = interpretedMeta;
}
++lineCount;
if (/^\s*$/.test(line)) {
lineReader.close();
}
});
lineReader.on("close", () => {
callback(meta);
process.exit(0);
});
}
where interpretMeta() is a function that formats the string given based on a linenumber. I will integrate this into readMeta() later since it's somewhat redundant.
Problem
This code works with one file, but bugs if it runs multiple times in a short amount of time. It reaches the second line but then starts over each time the function runs.
Im not 100% sure why this happens, but I assume something like lineReader.on()'s callback doesn't make copies of the variables it gets from readMeta happens. I can't figure out how to debug or solve though.
Fix
I have no experience whatsoever working with asynchronous functions, so apologies if i use the wrong terms onwards: I believe a way around my problem that i would be comfortable working with, is a synchronous readline() function that reads the next line in a stream. I can't figure out how to do this though, so my question is how do i:
A: fix the code
B: make a synchronous ´readline` function
Thanks

User O. Jones commented on the original post asking if this answer would answer my question. The aproved answer did not, but an answer below by User Lead Developer got me on the right track:
Update in 2019
An awesome example is already posted on official Nodejs documentation. here
This requires the latest Nodejs is installed on your machine. >11.4
const fs = require('fs');
const readline = require('readline');
async function processLineByLine() {
const fileStream = fs.createReadStream('input.txt');
const rl = readline.createInterface({
input: fileStream,
crlfDelay: Infinity
});
// Note: we use the crlfDelay option to recognize all instances of CR LF
// ('\r\n') in input.txt as a single line break.
for await (const line of rl) {
// Each line in input.txt will be successively available here as `line`.
console.log(`Line from file: ${line}`);
}
}
processLineByLine();
The page he links to contains another example that i ended up following, following is my final code:
async function readMeta (path) {
const meta = {};
const lineReader = require("readline").createInterface({input: fs.createReadStream(path)});
let currentLine = 0;
lineReader.on("line", line => {
switch (currentLine) {
case 0:
meta.name = line;
break;
case 1:
meta.tags = line.split(" ");
break;
case 2:
meta.type = line;
break;
case 3:
meta.id = +line;
}
++currentLine;
if (/^\s*$/.test(line)) {
lineReader.close();
}
});
await once(lineReader, "close");
return meta;
}
Thanks for the help.

Related

create a json dynamically in node js

I have a problem with creating a json dynamically. I retrieve the data of a sent json:
body.value look like :
{
hubspotProperty: 'question3',
response: 'Nous utilisons un google drive mais il est très peu utilisé'
},
{
hubspotProperty: 'question2',
response: 'Nous complétons le CRM quand nous avons ' +
'le temps et au bon vouloir du commercial'
},
{
hubspotProperty: 'question1',
response: "Nous n'avons aucun processus " +
"automatisé et nous n'en voyons pas " +
"l'intérêt"
}
]
but I want the hubspot property to be the key of the new json and the response to be my I don't now how I can do this .
Help me please
Bonjour, the simplest way to make json:
var someValue = "Jean"
var jsonString = `{"nom":"${someValue}"}`
Take care of the "backwards ticks" `
Regarding the json which comes in to you:
var exampleJson = `{ "hubspotProperty": "question3" }`
It is this easy ...
var args = JSON.parse(exampleJson)
var value = args.hubspotProperty
console.log("it's done .. " + value)

Firestore import data-file is not correct saved (ä,ö,ü,á are shown as symbols)

i try to import some data in my firestore, this works quite well.
The data are german, english and spanish languages, so I have á,í, ä,...
My .json file is correct and when i opened it via xcode and vs code everything looks fine.. When i start my import and take a look at my database, those letters will be replaced by icons...
And i dont know why ... Does anybody know how to fix this?
As an example this is my .json :
{
"exercisesTest" : [
{
"exercises": "a_01",
"description_d": "Stell dich schulterbreit hin und stütz deine Hände in die Hüfte. Senk deinen Oberkörper ab, so als wolltest du dich auf einen Stuhl setzen. Achte darauf, dass deine Füße während der ganzen Bewegung immer fest am Boden bleiben und sich immer über den Fußgelenken befinden.",
"description_e": "Take a stance with your feet shoulder-width apart and put your hands on your hips. Lower your upper body as if you were sitting on a chair. Make sure that your feet remain firmly on the ground during the whole movement. Your knees should always be above the ankles.",
"description_s": "Ponte en posición con los pies separados a la anchura de los hombros y pon las manos sobre las caderas. Baja la parte superior del cuerpo como si estuvieras sentado en una silla. Asegúrate de que tus pies permanezcan firmemente en el suelo durante todo el movimiento. Las rodillas deben estar siempre por encima de los tobillos."}]}
and this is my firebase db...
Import will be done by my .js file and works fine, except the icons :p
const admin = require('./node_modules/firebase-admin');
const serviceAccount = require("./serviceAccountKey.json");
const data = require("./data.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "xxxx"
});
data && Object.keys(data).forEach(key => {
const nestedContent = data[key];
if (typeof nestedContent === "object") {
Object.keys(nestedContent).forEach(docTitle => {
admin.firestore()
.collection(key)
.doc(docTitle)
.set(nestedContent[docTitle])
.then((res) => {
console.log("Document successfully written!");
})
.catch((error) => {
console.error("Error writing document: ", error);
});
});
}
});
Thanks for your help!
Okay, i deleted my whole .json file and create a new one with a different csv to json - now it works...
used this csv to json + validator
https://csvjson.com/json_validator

Change day/month language in DialogFlow bot

I'm creating an assistant for an italian restaurant using DialogFlow.
I've set the language to spanish, and everything seems to go fine, but when i show the final date of the reservation it is shown in english (Friday and May in attached picture's case).
Is it possible to change it?
This is the code that generates the above particular response to a table booking process:
function createBooking(agent) {
let guests = agent.parameters.comensales;
let time = new Date(agent.parameters.time);
let date = new Date(agent.parameters.date);
let bookingDate = new Date(date);
var numeroReserva = Math.random().toString(16).slice(2, 8).toUpperCase();
bookingDate.setHours(time.getHours());
bookingDate.setMinutes(time.getMinutes());
let now = new Date();
if (guests < 1){
agent.add('You need to reserve a table for at least one person. Please try again!');
} else if (bookingDate < now){
agent.add(`No puedes reservar una fecha pasada. Por favor, inténtalo de nuevo!`);
} else if (bookingDate.getFullYear() > now.getFullYear()) {
agent.add(`No puedes hacer una reserva para ${bookingDate.getFullYear()} todavía. Por favor, elige una fecha en ${now.getFullYear()}.`);
} else {
let timezone = parseInt(agent.parameters.time.toString().slice(19,22));
bookingDate.setHours(bookingDate.getHours() + timezone);
agent.add(`Perfecto. He reservado una mesa para ${guests} el ${bookingDate.toString().slice(0,21)}`);
agent.add(`Tu código de reserva es: ${numeroReserva}`);
agent.add('Nos vemos pronto!');
agent.add('Buon appetito!');
}
}
The code running the fulfillment runs within Google's compute infrastructure which has a default locale/language of US English. When a request arrives for fullfilment from Dialog flow, that request carries with it the language that we are to use to respond. See the languageCode in the Webhook Request JSON. When we use the APIs in Node.js, it looks like this data is available in the agent.locale property.
Looking at the JavaScript Date object, we seem to have a method on it called toLocaleString() which converts a date/time into a string but additionally supplies the language (locale) to be used to create the language specific content and format. If we put all this together, we might find that the following code line may work:
agent.add(`Perfecto. He reservado una mesa para ${guests} el ${bookingDate.toLocalString(agent.locale).slice(0,21)}`);
This may take a few tests to get right. I'd start by logging agent.locale as a test to ensure that it has the value we expect/hope.
It's late but for those who face with this problem. You know you can do it like this:
let options = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric',
hour12: false, hour: 'numeric', minute: 'numeric' };
var curr_date = new Date();
agent.add(`Sono le ` + curr_date.toLocaleString('it-IT', options));
If someone likes to use moment.js
you can add library to your project from fulfillment inline editor.
Go to Fulfillment, click on "package.json"
add the following lines in dependencies
"moment": "^2.24.0",
"moment-timezone": "^0.5.31"
Then back to index.js and add the following line to import it
const moment = require('moment-timezone');
and then you can handle like this in your function
if(agent.locale === 'en'){
moment.locale('en-US');
agent.add(`Now is ` + moment().tz(your_time_zone).format('LLLL'));
}
else if(agent.locale === 'it-IT' || agent.locale === 'it'){
moment.locale('it');
agent.add(`Sono le ` + moment().tz(your_time_zone).format('LLLL'));
}
The sample of response is " Sono le martedì 20 ottobre 2020 15:30"
or "Now is Tuesday, October 20, 2020 3:31 PM"

Deleting records on a database

I have a logging command which works perfectly, the only issue is I can not finish the off section of the command. I need it to delete both records (guildid, channel) if the guildid matches.
This is what I have tried.
if (args[0] === 'off') {
message.channel.send('Logging turned off!');
const del = db.prepare('DELETE FROM logging WHERE guildid = ?;');
del.run({
guildid: `${message.guild.id}`
});
return;
Looking at the photo, when the args off is ran, I need it to delete the guildid contents (495602...) and channel contents (<#5290...) if the guildid contents matches with the guild the command was ran in.
Your current answer is the incorrect way to use prepared statement. If you use the way you pose, you open yourself up to SQL injection because you aren't interpreting the value you want to use in the statement as a value, you use it as part of the overall statement and then run the statement without parameters. That means that I could potentially provide a value that might not do exactly what you think it will.
For example the following won't do anything,
const $rowid = "3 OR rowid = 4";
const deleteStatement = db.prepare("DELETE FROM lorem WHERE rowid = $rowid");
deleteStatement.run({$rowid});
deleteStatement.finalize();
But this will delete elements with rowid 3 or 4:
const $rowid = "3 OR rowid = 4";
const deleteStatement = db.prepare(`DELETE FROM lorem WHERE rowid = ${$rowid}`);
deleteStatement.run();
deleteStatement.finalize();
Instead, take a look at the sqlite3 documentation here.
You need to actually paramaterize your prepared statement like the following:
const sqlite3 = require("sqlite3").verbose();
const db = new sqlite3.Database(":memory:");
db.serialize(function() {
// Make the table
db.run("CREATE TABLE lorem (info TEXT)");
// Create some dummy data
const insertStatement = db.prepare("INSERT INTO lorem VALUES (?)");
for (let i = 0; i < 5; i++) {
insertStatement.run(`My Data ${i}`);
}
insertStatement.finalize();
// Delete some data
const deleteStatement = db.prepare("DELETE FROM lorem WHERE rowid = $rowid");
deleteStatement.run({
$rowid: 3
});
deleteStatement.finalize();
// Print elements
db.each("SELECT rowid AS id, info FROM lorem", (err, {id, info}) => console.log(`${id}: ${info}`));
});
db.close();
For anyone in the future looking how to do this, this was the answer.
EDIT: Can't mark as an answer until 2 days lol
if (args[0] === 'off') {
message.channel.send('Logging turned off!');
db.prepare(`DELETE FROM logging WHERE guildid = '${message.guild.id}'`).run();
return;
Late to the better-sqlite3 party:
const del = db.prepare('DELETE FROM logging WHERE guildid = ?');
del.run(message.guild.id)

find a particular page in orchard migration

So I am adding a page via the migration.cs file and this is working fine.
The issue comes that if I run the Update twice it will add the page twice.
While I know that in stage/prod this will not be the case.
But in dev we rerun the release code a few times to get it all working.
anyway what I want is something like
private readonly IContentManager _contentManager;
// this line is made up
var articleListingPage = _contentManager.GetPages.SingleOrDefault(p => p.Name == "ArticleListingPage");
if (articleListingPage == null)
{
var articlesPage = _contentManager.Create("Page");
articlesPage.As<TitlePart>().Title = "ArticleList";
articlesPage.As<BodyPart>().Text = #"<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus mauris magna, varius vel vulputate eget, bibendum id magna.</p>";
articlesPage.As<CommonPart>().Owner = _orchardServices.WorkContext == null ? null : _orchardServices.WorkContext.CurrentUser;
var articlesRoute = articlesPage.As<AutoroutePart>();
articlesRoute.DisplayAlias = _autorouteService.GenerateAlias(articlesRoute);
_autorouteService.PublishAlias(articlesRoute);
var menu = _menuService.GetMenu("Main Menu");
if (menu == null) throw new Exception("Could not get the menu. Please the name of the menu is correct.");
articlesPage.As<MenuPart>().Menu = menu;
articlesPage.As<MenuPart>().MenuText = "Article List YYYY";
articlesPage.As<MenuPart>().MenuPosition = "10";
_contentManager.Publish(articlesPage);
}
Does anyone know how to find a collections of pages/content?
I figured it out. As its dynamics its based on the Content Parts in the item.
In this example the "TitlePart" is added to the content type "Page"
var list = _contentManager.Query(VersionOptions.Published, "Page")
.List().Cast<dynamic>();
var existingPage = list.Any(i => i.TitlePart.Title == "ArticleList");
if (existingPage == false)
{
//add page etc
}

Resources