Object value undefined after csv-parser - javascript

I am unsure why do I get undefined after I run console.log(class1.roster), even though I have passed an array to that object in the previous command class1.importRoster(createRoster(filePath));. I would appreciate an explanation and a possible way to see class1.roster have a value. I am using the npm package csv-parser. Here is my MWE and the contents of test.csv
mwe.js
const fs = require ('fs');
const csv = require('csv-parser');
const filePath = 'test.csv'
let classRoster = [];
function schoolClass (subject, period, schoolYear) {
this.subject = subject;
this.period = +period;
this.schoolYear = schoolYear;
this.importRoster = function (roster) {
this.roster = roster;
}
}
function Student (firstName,lastName,ID,grade,period) {
this.firstName = firstName;
this.lastName = lastName;
this.ID = ID;
this.grade = grade;
this.period = period;
}
function createRoster (path) {
fs.createReadStream(path)
.pipe(csv())
.on('data', (data)=>{
classRoster.push(new Student(data.first_name, data.last_name, data.id, data.grade, data.period))
})
.on('end', ()=>{
console.log("Done");
return classRoster;
})
}
class1 = new schoolClass("GER", 3, "19-20");
console.log(class1);
class1.importRoster(createRoster(filePath));
console.log(class1.roster);
test.csv
first_name,last_name,grade,id,period
Blondy,Debrett,10,0217842058,3
Nester,Langstrath,10,2574570346,3
Waldon,Trunby,11,4785462996,8
Lark,Alsopp,11,0039229732,7
Almira,Baistow,12,1272978281,3
Carmela,Abberley,12,7279500295,8
Cristabel,Soanes,10,3086318686,5
Currie,Milton-White,11,8123679871,8
Alexei,Everist,11,2538149622,7
Lina,Hehir,9,1345944671,3

You're not returning anything here:
function createRoster (path) {
fs.createReadStream(path)
.pipe(csv())
.on('data', (data)=>{
classRoster.push(new Student(data.first_name, data.last_name, data.id, data.grade, data.period))
})
.on('end', ()=>{
console.log("Done");
return classRoster;
})
}
Here you are calling the stream's on function and passing two arguments - the event to listen for and a function to run when that event is emitted by the stream. The return value of that anonymous function is not consumed by anything.
.on('end', ()=>{
console.log("Done");
return classRoster;
})
There's some other potential pitfalls here, namely a global classRoster variable that you are pushing data to. If you want this to be a global cache, there is no reason to ever call importRoster as you can just refer to it directly. However, calling createRoster more than once will continue to push data to the same array.
It looks like you should probably restructure your code to expect an asynchronous format. You can use the library's sync API but this might cause performance issues depending on your use case: https://csv.js.org/parse/api/sync/
Because of its simplicity, this is the recommended approach if you don't need scalability and if your dataset fit in memory. It is much easier to use at the expense of not being scalable.

Related

Python Function in Javascript Package

I have a python file that holds many subsidiary methods and one essential function calling those subsidiary functions. The essential function takes a string as input and spits out a 2D array. However, I need to use it in the web development project. How do I pass on the user-input string to the python file via JS and then get the output in a JSON format back from the python file?
python file's essential function
def foo(str, num):
bar = doSomething(temp, str)
raw_output = doAction(bar)
final_output= doRefining(raw_output[:num], 10)
return final_output
What I tried in index.js
const getRes = () => {
const child_process = require('child_process');
child_process.spawn('python', ['pythonFile.py']);
process.stdout.on('data',function(response){
var output = response;
return output;
});
}
Your code should work, with only one important modification: instead of doing return output, you should use it. I usually do that by creating a buffer variable outside the callback's scope, and appending the data to it as it arrives.
Then, on the close event, you can use the data, or return it using a promise/callback.
An example:
const child_process = require('child_process');
const getRes = callback => {
child_process.spawn('python', ['pythonFile.py']);
let result = '';
process.stdout.on('data', data => {
result += data;
});
process.on('close', () => {
callback(result);
});
}
// Example usage:
getRes(result => {
console.log('Output:', result);
});

Why is video not displaying when user joins room?

So, I have an adaptation of https://github.com/Dirvann/mediasoup-sfu-webrtc-video-rooms working in vanilla JS that I am attempting to adapt to use React. Instead of every user being a broadcaster, in my version, only the room creator is the broadcaster.
I've hit an issue. In the React version, when a viewer navigates to the room, they are not receiving the stream! I have no idea why since they use the same RoomClient class: https://github.com/Dirvann/mediasoup-sfu-webrtc-video-rooms/blob/master/public/RoomClient.js
This line const consumer = await this.consumerTransport.consume({ id, producerId, kind, rtpParameters, codecOptions, }); seems to be causing the problem, since the log following it doesn't get printed. Inside the consume function, my log that says 'hi' is executed, but 'blah' is not.
Here is a screenshot of the client console:
The most important functions are found below. For the entire class, please click the github link above.
async consume(producer_id) {
//let info = await roomInfo()
console.log('consume ', producer_id);
console.log('dddddddddddd', await this.getConsumeStream(producer_id));
this.getConsumeStream(producer_id).then(
function ({ consumer, stream, kind }) {
console.log('blah');
this.consumers.set(consumer.id, consumer);
let elem;
console.log('clg kind === ', kind);
if (kind === 'video') {
console.log('cons vid');
elem = document.createElement('video');
elem.srcObject = stream;
elem.id = consumer.id;
elem.playsinline = false;
elem.autoplay = true;
elem.className = 'vid';
this.remoteVideoEl.appendChild(elem);
} else {
elem = document.createElement('audio');
elem.srcObject = stream;
elem.id = consumer.id;
elem.playsinline = false;
elem.autoplay = true;
this.remoteAudioEl.appendChild(elem);
}
consumer.on(
'trackended',
function () {
this.removeConsumer(consumer.id);
}.bind(this)
);
consumer.on(
'transportclose',
function () {
this.removeConsumer(consumer.id);
}.bind(this)
);
}.bind(this)
);
}
async getConsumeStream(producerId) {
const { rtpCapabilities } = this.device;
console.log('rtpcaps ', rtpCapabilities);
const data = await this.socketRequest('consume', {
rtpCapabilities,
consumerTransportId: this.consumerTransport.id, // might be
producerId,
}).then((data) => {
console.log('daaatttaaa', data);
return data;
});
const { id, kind, rtpParameters } = data;
console.log('data === ', data);
let codecOptions = {};
console.log('aaaaaaaaaaaaaa', this.consumerTransport.consume);
const consumer = await this.consumerTransport
.consume({
id,
producerId,
kind,
rtpParameters,
codecOptions,
})
.then((result) => {
console.log('bbbbbbb', result);
return result;
});
console.log('consumer === ', consumer);
const stream = new MediaStream();
console.log('stream === ', stream);
stream.addTrack(consumer.track);
console.log('kind ', kind);
return {
consumer,
stream,
kind,
};
}
Many thanks for your time
---update---
This line never resolves: const consumer = await this.consumerTransport.consume({ id, producerId, kind, rtpParameters, codecOptions, })
It ends up executing some complex-looking functions from packages. In fact, It seems to get stuck in an infinite loop in the package called sdp-transform, after executing a few lines in mediasoup-client.
I don't even understand how chrome debugger's working. Because if I put a breakpoint on that line where ...consume(... is called, and click 'step', it parses this line: let answer = await this._pc.createAnswer() ... Which is part of a function called receive, which wasn't called by the breakpoint line... Please, somebody help.
You said something interesting there. 'hi' gets logged and 'blah' not. There isn't much code in between them. This should make us wonder how it is even possible.
Maybe create the same just without any distractions around.
Promise.resolve()
.then(console.log('hi'))
.then(function() {
console.log('blah');
})
What would that do? and why?
.then is a function that expects to be given a function (to be called when the promise resolves...)
What does console.log('hi') return? And when is it executed?
You did not want to execute the log when creating the promise chain and you also want to maybe return the result again since you want to work with the value.
So my guess would be. Changing it to:
.then(result => {
console.log('hi')
return result
})
brings you at least a step closer.

Extracting data from an object inside of another function without writing to JSON?

I'm working on writing a Discord bot with music functionality using discord.js and node, along with a handful of other packages like yt-search and ytdl-core.
The problem I'm trying to solve is related to the code below (newVar was just a placeholder while testing):
let regex = /^https/i;
let isUrl = regex.test(checkUrl);
let songInfo;
if (!isUrl) {
yts(suffix, function (err, r) {
if(err) console.error(err);
const videos = r.videos;
let data = JSON.stringify(videos[0])
fs.writeFileSync('youtube.json', data)
})
let newVar = require('../youtube.json');
let {url, title} = newVar;
songInfo = await ytdl.getInfo(newVar.url)
} else {
songInfo = await ytdl.getInfo(args[1]);
}
const song = {
title: songInfo.title,
url: songInfo.video_url,
};
What I'm trying to do,
Is to check whether or not the 'suffix' is a URL, and if not, run suffix through the yts() (yt-search) function, and get the URL from the returned object.
Then pass that url value through the ytdl.getInfo() function.
It works as intended to an extent, but writing to the JSON is causing a problem in that it is returning the same URL even when a new search is completed, until the program is restarted,
Then it will repeat the process with whatever value was stored in the JSON file when the program was executed. However, I get the results when I console.log(videos[0].url), and the value changes with each query, but I have no way to pass that data outside of the yts() function without writing to the JSON first.
Any ideas?
I'm sorry if I'm not specific enough, or confused in my understanding, this is one of my first "complex" projects. It could also be that the issue exists elsewhere in the module, but from what I've done so far I think it's somewhere in the code shown above. Thanks!
here is something you can do to get it right.
const getSongInfo = (url, suffix) => {
return new Promise(async (resolve, reject) => {
let regex = /^https/i;
let isUrl = regex.test(url);
if (!isUrl) {
// netween where is the suffix variable ?
yts(suffix, async (err, r) => {
if(err) reject(err);
const videos = r.videos;
let data = JSON.stringify(videos[0]);
// still don't know why bother save it and access it again.
fs.writeFileSync('youtube.json', data);
let newVar = require('../youtube.json');
resolve(await ytdl.getInfo(newVar.url));
});
} else {
resolve(await ytdl.getInfo(args[1]));
}
});
}
// hope the outer function is async
let songInfo = await getSongInfo(checkUrl, suffix);
const song = {
title: songInfo.title,
url: songInfo.video_url,
};
Between make sure to check that suffix variable which is not in scope.

How to run a firebase function correctly?

This function will return the value of initiate_session, initiate_session[user_session].events, user_session
before the firebase function runs.
How to run the firebase function first ??
function createSession() {
var user_session = randomCharacters(20) //8iKhA4Aq2!6gZ)890ip#;
var ip = '127.0.0.1';
var initiate_session = new Object();
var session_started = new Date().getTime();
firebase.database().ref(user_session).once('value', function (snapshot) {
if (snapshot.exists()) {
console.log('session exists');
createSession();
} else {
console.log('session not exists')
initiate_session[user_session] = {
ip: ip,
session_started: session_started,
events: {}
};
firebase.database().ref(user_session).set({
ip: ip,
session_started: session_started,
events: {}
});
}
});
console.log('new session', initiate_session);
return [initiate_session, initiate_session[user_session].events, user_session];}
This is because the Firebase function is asynchronous - the code inside the method that gets the snapshot is a callback that is executed after the Firebase read is finished, which may take several seconds. However, as soon as you dispatch the read request via .once(...) your execution flow continues and the return is called.
There are a few possible solutions:
Pass a callback argument to your createSession() method that is called with the values you are trying to return, instead of returning them directly.
Return a promise from your method that resolves to give the values you're trying to return.
Use async/await syntax for your Firebase call. This is covered already in this question: running queries in firebase using async / await
Rough Example of #1
function createSession(onSessionCreated) {
var user_session = "whatever";
var initiate_session = new Object();
firebase.database().ref(user_session).once('value', function (snapshot) {
// do things with the snapshot
onSessionCreated(initiate_session, initiate_session[user_session].events, user_session)
});
}
// usage:
createSession(function (initiate_session, events, user_session) {
// do things with initiate_session, events and user_session
});
Rough Example of #2
function createSession() {
var user_session = "whatever";
var initiate_session = new Object();
firebase.database().ref(user_session).once('value').then(function (snapshot) {
// do things with the snapshot
return [initiate_session, initiate_session[user_session].events, user_session];
});
}
// usage:
createSession().then(function (results) {
// do things with results (i.e. the three elements in the array above)
});
Rough Example of 3
async function createSession() {
var user_session = "whatever";
var initiate_session = new Object();
const snapshot = await firebase.database().ref(user_session).once('value');
// do things with the snapshot
return [initiate_session, initiate_session[user_session].events, user_session];
}
// usage:
const results = await createSession();
If you're new to async/await code it probably won't be the easiest place to start as it may require changes elsewhere in your code, but this article is a good resource if you're keen to learn.

node.js: structure multiple API requests, work on them and combine them

currently I am struggeling a little bit with node.js (I am new to it) doing different API requests (Usabilla API), work on the results and then combine them in order to work on the whole set (e.g. export).
Requesting the API is not the problem but I can't get the results out to do some other stuff on it (asynchronous code drives me crazy).
Attached please find a overview how I thought to do this. Maybe I am totally wrong about this or maybe you have other more elegant suggestions.
My code works until I have to request the two different API "adresses" (they are provided) and then extract the results to do some other stuff.
My problem here is that there are nested functions with a promise and I cant figure out how to pass this through the parent function inside waterfall to get handled by the next function.
In the code, of course there is nothing parallel as shown in the diagram.
Thats another point, how to do that ? Simply nest parallel and series/ another waterfall inside waterfall ?
I am a little bit confused because that gets more and more complex for a simple problem when this would be done with synchronous code.
Here I build up all my request querys (at the moment 4):
function buildQuery(IDs,callback){
var i = 0;
var max = Object.keys(IDs).length;
async.whilst(
function(){return i < max},
function(callback){
FeedbackQuery[i] =
{
identifier: IDs[i].identifier,
query:
{id: IDs[i].id,
params: {since:sinceDate,}
}
};
i++;
callback(null,i);
})
console.log(FeedbackQuery);
callback (null,FeedbackQuery);
};
I then have to decide which type of query it is and add it to an object which should contain all the items of this identifier type:
function FeedbackRequest(FeedbackQuery,callback)
{
var i = 0;
var max = Object.keys(FeedbackQuery).length;
async.whilst(
function(){return i < max},
function (callback){
identifier = FeedbackQuery[i].identifier;
APIquery = FeedbackQuery[i].query;
switch(identifier)
{
case 'mobilePortal':
console.log(FeedbackQuery[i].identifier, 'aktiviert!');
var result = api.websites.buttons.feedback.get(APIquery);
result.then(function(feedback)
{
var item = Object.keys(feedbackResults).length;
feedbackResultsA[item] = feedback;
callback(null, feedbackResultsA);
})
break;
case 'apps':
console.log(FeedbackQuery[i].identifier, 'aktiviert!');
var result = api.apps.forms.feedback.get(APIquery);
result.then(function(feedback)
{
var item = Object.keys(feedbackResults).length;
feedbackResultsB[item] = feedback;
callback(null, feedbackResultsB);
})
break;
}
i++;
callback(null,i);
})
};
Currently the functions are bundled in an async waterfall:
async.waterfall([
async.apply(buildQuery,IDs2request),
FeedbackRequest,
// a function to do something on the whole feedbackResults array
],function (err, result) {
// result now equals 'done'
if (err) { console.log('Something is wrong!'); }
return console.log('Done!');
})
How it actually should be:
Structure
Thank you very much for any tips or hints!
I'm not proficient with async, and I believe if you'r new to this, it's harder than a simple Promise library like bluebird combine with lodash for helpers.
What I would do based on your schemas :
var firstStepRequests = [];
firstStepRequests.push(buildQuery());// construct your first steps queries, can be a loop, goal is to have firstStepRequests to be an array of promise.
Promise.all(firstStepRequests)
.then((allResults) => {
var type1 = _.filter(allResults, 'request_type_1');
var type2 = _.filter(allResults, 'request_type_2');
return {
type1: type1,
type2: type2
};
})
.then((result) => {
result.type1 = //do some work
result.type2 = //do some work
return result;
})
.then((result) => {
//export or merge or whatever.
});
Goal is to have a simple state machine.
UPDATE
If you want to keep identifier for a request, you can use props to have :
var props = {
id_1:Promise,
id_2:Promise,
id_3:Promise
};
Promise.props(props).then((results) => {
// results is {
id_1:result of promise,
id_2:result of promise,
etc...
}
})
You could do something like :
var type1Promises = getType1Requests(); //array of type 1
var type2Promises = getType2Requests(); // array of type 2
var props = {
type_1: Promise.all(type1Promises),
type_2: Promise.all(type2Promises)
}
Promise.props(props).then((result) => {
//result is : {
type_1: array of result of promises of type 1
type_2: array of result of promises of type 2
}
})

Categories

Resources