1

I have 2 files in Node js .I want to merge these 2, but I am facing problem.. This file calls function from python file

const app = express()

let runPy = new Promise(function(success, nosuccess) {

    const { spawn } = require('child_process');
    const pyprog = spawn('python', ['./ml.py']);

    pyprog.stdout.on('data', function(data) {

        success(data);
    });

    pyprog.stderr.on('data', (data) => {

        nosuccess(data);
    });
});

app.get('/', (req, res) => {

    res.write('welcome\n');

    runPy.then(function(testMLFunction) {
        console.log(testMLFunction.toString());
        res.end(testMLFunction);
    });
})
app.listen(4000, () => console.log('Application listening on port 4000!'))

python file ml.py

def testMLFunction():
return "hello from Python"

print(testMLFunction())

Below file works on button click with post method

var fs = require('fs');

var server = http.createServer(function (req, res) {

    if (req.method === "GET") {
        res.writeHead(200, { "Content-Type": "text/html" });
        fs.createReadStream("./form.html", "UTF-8").pipe(res);
    } else if (req.method === "POST") {

        var result = "";
        req.on("data", function (chunk) {
            console.log(chunk.toString());

            result = chunk;
            //body=body.toUpperCase;
        });

        req.on("end", function(){
            res.writeHead(200, { "Content-Type": "text/html" });
            res.end(result);
        });
    }

}).listen(3000);

how can I do that..

kriti
  • 145
  • 3
  • 4
  • 13

1 Answers1

0

There are several things wrong here. I will explain as plain as possible.

  1. You forgot to add in your code var express = require('express')
  2. The promise you made, runPy, must be wrapped in a function, whereas your approach will instantly start the promise upon loading the script itself.
  3. You are resolving/rejecting on first incoming output, you shouldn't do that because you won't be able to know what really happened in the shell. You need to store those output lines, this is the only way of you knowing what the script tells you.
  4. In runPy you must resolve/reject upon pyprogr close event.
  5. You cannot access directly the method of another script, no matter what that kind of file that is a py, sh, bat, js. However, you can access internal functions of it by passing arguments to the shell, and of course, that script must have the logic required to deal with those arguments.
  6. When using spawn/exec you must keep in mind that YOU ARE NOT the user executing the script, the node user is, so different outcomes may occur.
  7. Most importantly, your targeted script must PRINT/ECHO to shell, no returns! The best approach would be to print some json string, and parse it in javascript after the shell is closed, so you can have access to an object instead of a string.

Below you will find a demo for your use case, i changed the python file so it can print something.

ml.py

print('I\'m the output from ml.py')

index.js

const express = require('express');
const app = express()

let runPy = function () { // the promise is now wrapped in a function so it won't trigger on script load
    return new Promise(function (success, nosuccess) {

        const {spawn} = require('child_process');
        const pyprog = spawn('python', ['./ml.py'], {shell: true}); // add shell:true so node will spawn it with your system shell.

        let storeLines = []; // store the printed rows from the script
        let storeErrors = []; // store errors occurred
        pyprog.stdout.on('data', function (data) {
            storeLines.push(data);
        });

        pyprog.stderr.on('data', (data) => {
            storeErrors.push(data);
        });
        pyprog.on('close', () => {
            // if we have errors will reject the promise and we'll catch it later
            if (storeErrors.length) {
                nosuccess(new Error(Buffer.concat(storeErrors).toString()));
            } else {
                success(storeLines);
            }
        })
    })
};

let path = require('path');
app.use(express.json());
app.use(express.urlencoded({ extended: true })); // you need to set this so you can catch POST requests
app.all('/', (req, res) => { // i've change this from .get to .all so you can catch both get and post requests here

    console.log('post params', req.body);

    if(req.body.hasOwnProperty('btn-send')){

        runPy()
            .then(function (pyOutputBuffer) {

                let message = 'You sent this params:\n' +JSON.stringify(req.body, null,2) + '\n';
                message += Buffer.concat(pyOutputBuffer).toString();

                res.end(message);

            })
            .catch(console.log)
    }else{
        res.sendFile(path.join(__dirname,'form.html')); // you need an absolute path to 'file.html'
    }

});
app.listen(4000, () => console.log('Application listening on port 4000!'));

form.html

<div>hello there</div>
<form action="/" method="post">
    <input type="text" value="" name="some-text"/>
    <button type="submit" value="1" name="btn-send" >Press me!</button>
</form>
darklightcode
  • 2,738
  • 1
  • 14
  • 17
  • Thanku so much for your beautiful explanation...I understood where was I wrong..but one more question..can we do all this on a button click from HTML..is there any way..or other method/procedure.. – kriti Feb 26 '20 at 09:11
  • You need to catch a post request. I have updated my answer to use `form.html`, which contains the button (just copy from `let path` below). This is just an example, i would advice you to check out templating engines such as `ejs` which is very popular with express and it will help you bind values directly to your html code. – darklightcode Feb 26 '20 at 09:37