Python Function in Javascript Package - javascript

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);
});

Related

Object value undefined after csv-parser

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.

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 JSON files and function on order

In my code, I want to load 2 JSON files first, then based on the result of the them, run another two JSON files, and then run a sequence of functions such as render, DOM etc. I want to save the JSON data in variable so I can refer to them later in the code.
Something like this:
$.when(parseJSON1(), parseJSON2())
.then(
parseJSON3(station_data.dj), parseJSON4(station_data.songurl)
)
.then(
_cacheOptions
)
.then(
_cacheDom
)
.then(`enter code here`
_events
).then(
_render
);
});
var station_data, history_data, itunes_data, coverList_data;
// Core Functions
function _cacheOptions() {
station_data = stationInfo[0];
history_data = stationHistory[0];
itunes_data = itunesInfo[0];
coverList_data = coverInfo[0];
}
function _cacheDom() {}
function _events() {}
function _render() {}
// Functions
function parseJSON1() {
return $.getJSON(settings.JSON1);
}
function parseJSON2() {
return $.getJSON(settings.JSON2);
}
function parseJSON3(searchTerm) {
return $.getJSON(settings.JSON3);
}
function parseJSON4() {
return $.getJSON(settings.JSON4);
}
So to make it simple, I want to run JSON1 and JSON2, then save its data as variables, then based on that data run JSON3 and JSON4 and save their variables. Then run the rest of the main functions.
The above would be the backbone of the plugin and I am trying to keep it very structural that everything runs on order.
Any idea how to make it work?
Answer:
You can use $.when in combination with $.getJSON like you have been, however it would be best to wrap this into a async function so that you don't have to worry about so many moving parts.
Create a store object for returned json.
Get the first two datasets
Put data in store
Check on your first two returned datasets
if the check passes continue the promise chain
Get the last two datasets with a $.when call
Put data in store
Return store
do something afterwards by using getAll().then(fn)
async function getAll() {
let json_store = {},
combine = (...locations) => locations.map($.getJSON),
json_check = (first, second) => (first.userId && second.userId);
await $.when(...combine(settings.JSON1, settings.JSON2)).then(function([first], [second]) {
json_store = Object.assign(json_store, {
first,
second
});
if (json_check(first, second)) {
return $.when(...combine(settings.JSON3, settings.JSON4)).then(function([third], [fourth]) {
json_store = Object.assign(json_store, {
third,
fourth
});
});
}
})
return json_store;
};
getAll().then(console.dir);
Example:
let settings = {
JSON1: "https://jsonplaceholder.typicode.com/todos/1",
JSON2: "https://jsonplaceholder.typicode.com/todos/2",
JSON3: "https://jsonplaceholder.typicode.com/todos/3",
JSON4: "https://jsonplaceholder.typicode.com/todos/4"
}
async function getAll() {
let json_store = {},
combine = (...locations) => locations.map($.getJSON),
json_check = (first, second) => (first.userId && second.userId);
await $.when(...combine(settings.JSON1, settings.JSON2)).then(function([first], [second]) {
json_store = Object.assign(json_store, {
first,
second
});
if (json_check(first, second)) {
return $.when(...combine(settings.JSON3, settings.JSON4)).then(function([third], [fourth]) {
json_store = Object.assign(json_store, {
third,
fourth
});
});
}
})
return json_store;
};
getAll().then(console.dir);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>

Writing to file in NodeJS calling from another JS file

I am having a weird issue writing to a file in NodeJS.
I have this code in my FileHandler.js:
module.exports.writeFile = function (arr) {
var fs = require('fs');
console.log(arr);
var file = fs.createWriteStream(__dirname+'\\test.txt',{encoding: 'utf8'});
file.on('error', function (err) {
console.log(err); });
file.on("finish", function() {
console.log("finished");
});
arr.forEach(function (item) {
file.write(item+"\n");
});
file.end();
}
If I append
exports.writeFile(["1","2","3"])
To the end of this file and then run node FileHandler.js
The file is created correctly.
However, if I call the writeFile function from another .js file as:
var R = require("r-script");
const dataHandler = require("./DataHandler");
const fileHandler = require("./FileHandler");
var out = R(__dirname + "\\apriori.R");
exports.getRules = function () {
dataHandler.getListOfPageVisitsBySession(1000781912582,1530781912582,function (result){
//ignored result variable
fileHandler.writeFile(["1","2","3"]);
})
}
and passing the exact same array to the function it doesn't write anything (but the file is created), neither fires err or finish event.
If it matters, the DataHandler method contains a request module and a GET to another API.
Any clue of the problem?
Thanks in advance

node.js - grunt - return parsed data from callback

I want to use API blueprint and make it automatically with grunt. I want to read apiary (tool for API blueprint) file, parsing it (with Protagonist which is API Blueprint Parser for Node.js), stringify it to the JSON format and write into another file. It is a simple task but I don't know how to do it, I always get undefined result. Here is what I have so far:
grunt.registerTask('apiary2js', 'Generate js version of apiary file.', function () {
var parser = require('protagonist');
var content = grunt.file.read('apiary.apib');
var blueprint = parser.parse(content, function (error, result) {
if (error) {
console.log(error);
return;
}
return result.ast; <-- (how to return this value?)
});
var json = JSON.stringify(blueprint);
grunt.file.write('test/frontend/apiary.js', "var apiary = " + json);
});
And result in apiary.js is this:
var apiary = undefined
The problem you're running into is that the parser.parse() method accepts a callback which executes asynchronously. You can't return a value from a callback as you would in a synchronous method because you don't know when it will be executed. The solution is to place the 'return' logic in the callback.
grunt.registerTask('apiary2js', 'Generate js version of apiary file.', function () {
var parser = require('protagonist');
var content = grunt.file.read('apiary.apib');
// Parse the contents of the file & execute the callback when done parsing.
parser.parse(content, function (error, result) {
if (error) {
console.log(error);
return;
}
// Now we can use the result as desired.
var json = JSON.stringify(result.ast);
grunt.file.write('test/frontend/apiary.js', "var apiary = " + json);
});
});

Categories

Resources