Live logging of a Python child process on an Electron app - python

I'm building an Electron-Python application. I'm using print statements (python-side) to distinguish different "phases" of the pipeline and I'd like to show these up on the UI, but I'm struggling to find an approach that works. Basically, I can't understand how to send data to the browser console every time that the "data" event is emitted from the child process.
Among other things, I've tried exposing ipcRenderer through contextBridge and messing with webContents.send, but I haven't been able to get anything through any of these methods, and being a beginner to Electron doesn't help.
Here's the handler:
// retrieve video data
ipcMain.handle('get-games', async (event, arg) => {
const pythonProcess = spawn('python', ["./backend/predict_games.py", arg]);
// sets pythonProcess as a global variable
global.childProcess = pythonProcess;
return new Promise((resolve, reject) => {
let result = "";
pythonProcess.stdout.on('data', async (data) => {
data = String(data);
if (data.startsWith("{"))
result = JSON.parse(data);
});
pythonProcess.on('close', () => {
resolve(result);
})
pythonProcess.on('error', (err) => {
reject(err);
});
})
});
Does anyone have any clue? If available, I'm up to go with a completely different approach.

Related

Vanilla Nodejs trigger Python script in new terminal

I want Nodejs to run a Python script in a new terminal.
I have a python script named test.py which simply does print('Hello World')
However, when I enter localhost:8080/python_run/ in my browser, I do not know if the script is run.
I can know the output using python.stdout.on('data', (data) => {console.log(`stdout: ${data}`);})
but I want to be able to monitor a large complex python script running which would give many print statements throughout.
I want it to display in a separate terminal instead of seeing only the std.out in the nodejs terminal.
I also wish to only implement these using vanilla nodejs.
The spawn child process of nodejs does not seem to let me run in a separate terminal.
How can I trigger a long complex and probably resource intensive Python script to run with the nodejs server as long as it receives a request (GET request of the url in this case)? If possible I'd also want the Python script to communicate back to the nodejs server once the Python script has finished running, to trigger some change of display of a website.
My current code
const {parse} = require('querystring');
const http = require('http');
const path = require('path');
const fs = require('fs');
var url = require("url");
const {spawn} = require('child_process');
const spawn1 = require('child_process').exec;
const requestHandler = (req, res) => {
fs.readFile("index_noexp.html",(err, data) => {
if(!err) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.end(data);
}
});
if (/python_run/i.test(req.url)) {
console.log('Now running the dangerous python script');
try {
const { spawn } = require('node:child_process');
var python = spawn('python', ['/Users/damian/nodetest/test.py']);
// var python = spawn1('python', [__dirname + '/test.py']);
python.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
})
} catch (error) {
console.error(error);
} finally {
console.log('Not sure what happened');
}
}
const server = http.createServer(requestHandler);
server.listen(8080);

how do I consume django api from react frontend

I'm new to React, I've written a Django API endpoint to perform some kind of sync. I want this API to be called from a React page, on clicking a TextLink - say Sync Now, how I should go about doing it? Based on the response from the API(200 or 400/500), I want to display Sync failed, on successful sync, I want to show Sync Now again, but with another text Last Sync Time: DateTime(for this I've added a key in my Django model), how can I use this as well.
Also, I've a followup, say instead of Sync Now and Synced we have another state Syncing, which is there until a success or failure is returned, Is polling from the server is a good option, or is there any other way. I know of websockets but not sure which can be used efficiently.
I've been stuck here from 3 days with no real progress. Anything will help. Thanks in advance.
I'd do something like that:
// Store last sync date in state with default value null
const [lastSyncDate, setLastSyncDate] = useState(null);
const [isLoading, setIsLoading] = useState(false);
const handleApiCall = async () => {
try {
setIsLoading(true);
const response = await fetch('api.url');
if (response.status === 200) {
const currentDate = Date.now().toString();
setLastSyncDate(currentDate);
}
}
catch(error) {
// handle error here
}
finally {
setIsLoading(false);
}
}
if (isLoading) {
return 'Loading...';
}
return (
<div onClick={() => handleApiCall()}>
{lastSyncDate !== null ? `lastSynced: ${lastSyncDate}` : "Sync now"}
</div>
)

How can I get the console output in Angular webpage?

I have a Django project which is using Angular as frontend. I have a button which on clicking is scanning the tables in the database. I have some print statements views.py file which is printing the scanned results constantly in the IDE console. I want that output in the webpage. I want that live printing of the console output in the frontend. Can any one know how i can achieve this?
You can achieve this by using server sent events. python can push these console logs to the frontend. Not a expert of python so giving a link below to how to send server side events from python to frontend
https://medium.com/code-zen/python-generator-and-html-server-sent-events-3cdf14140e56
In frontend you can listen to url exposed and as soon as server will push any message on this stream frontend can receive it and push it into component's array and can display over ui.
for frontend code, i am giving a minimal example below :-
import { Injectable, NgZone } from "#angular/core";
import { Observable } from "rxjs";
#Injectable({
providedIn: "root"
})
export class SseService {
constructor(private _zone: NgZone) {}
getServerSentEvent(url: string): Observable<any> {
return Observable.create(observer => {
const eventSource = this.getEventSource(url);
eventSource.onmessage = event => {
this._zone.run(() => {
observer.next(event);
});
};
eventSource.onerror = error => {
this._zone.run(() => {
observer.error(error);
});
};
});
}
private getEventSource(url: string): EventSource {
return new EventSource(url);
}
}
you can susbcribe to getServerSentEvent in above method and can continuously receive new messages, which is in your case your console logs.
You can try calling the following function with the information needed to be displayed.
addItem(val:any) {
let node = document.createElement("li")
let textnode = document.createTextNode(val)
node.appendChild(textnode)
document.getElementById("output").appendChild(node)
}
Make sure to have an element with the id="output".

Nodejs child process wont return data from python script to stdout

I'm writing a simple web app to get a handle on how node child processes work. Basically you enter your name into an angular frontend which passes the name to an express backend in a javascript object. The backend passes the name as an argument to a simple python script as a command line argument using the child_process module. Theres nothing wrong with the front end and the name gets successfully passed to the backend but when I call stdout on the python process it doesn't work. There aren't any error messages.
Heres the express code:
app.post('/api', (req, res, next) => {
console.log(req.body.name);
const spawn = require('child_process').spawn;
const process = spawn('python3', ['./hello.py', req.body.name]);
process.stdout.on('data', (data) => {
console.log(data);
res.status(200).json({greeting: data});
});
});
I put two console.log statements in the code to debug it. The first one prints the data but the second one inside the stdout function isn't called at all. The python script is in the same folder as the app.js express file so I'm pretty sure theres nothing wrong with the file path.
Here's the python script:
import sys
print('hello' + sys.argv[1])
sys.stdout.flush()
When I run it in the command line it works exactly as expected but I included it anyway just in case.
Process.stdout.on will keep on streaming until the end event. The code is wrong because you are actually sending response for every time there is some value in stdout. And you cant set the response header more than once. Try writing the code in below way. Thanks
let output;
Process.stdout.on("data", (data) => {
output += data;
});
Process.on("end", () => {
// send response here
});
close will trigger when your data completes
app.get("/list-account", async (req, res) => {
try {
let array = "";
let response = await child.spawn(
path.join(__dirname, "xcv-alpha-keychain.exe"),
["listaccounts"]
);
await response.stdout.on("data", (data) => {
const stdout = data.toString();
console.log("stdout", stdout);
array += stdout;
// return res.status(200).json({ array });
});
response.stderr.on("data", (data) => {
return res.status(500).send(data.toString());
});
response.on("error", (error) => {
return res.status(500).send({ error });
});
response.on("close", (code) => {
return res.status(200).json({ array, code });
});
} catch (error) {
return res.status(500).send(["a", "b", "c"]);
}
});
Instead of
console.log(data)
use
console.log(data.toString())

how to do somthing before an HTTP POST request in node.js?

I'm very new to nodejs what I try to do is upload an image with some data to nodejs API but before saving data to Mongo DB I try to do some process to this uploaded image using Python class then I will save the results to DB
so can I send uploaded image to python code and waite the result before saving any data to DB
my code is here
router.post('/img2text', upload.single('photo'), (req, res) => {
// Create the object for insertion into database
const Img2text = new img2text({
imgPath: req.file.path,
sender: req.body.sender,
processed: 0,
resultText: "no result",
});
/////////////////////////////////////////////////////////////start
var exec = require("child_process").exec;
exec(`python uploads/hello.py ${req.file.path}`,(error, stdout, stderr) => {
if (error) {
Img2text.processed=0;
Img2text.resultText='no result'
console.error(`exec error: ${error}`);
return;
}
console.log(`stdout: ${stdout}`);
Img2text.processed=1;
Img2text.resultText=stdout.toString('utf8');
console.log(`stderr: ${stderr}`);
console.log(req.query.firstname);
});
/////////////////////////////////////////////////////////end
// Save into database
Img2text.save((err) => {
// Check if error
if (err) {
res.json({ success: false, message: err });
} else {
console.log("img saved !!")
res.json({ success: true, message:img.resultText }); // Return success message
}
});
});
if the python code takes so long time my object will be empty?
any answer will be greatly appreciated
Have you tried to use Promise,If not refer this link Promise MDN. Just wrap your process in a Promise and when you finish with your process resolve it and save it to database.
router.post('/img2text', upload.single('photo'), (req, res) => {
// Receive your image
let pythonProcess = new Promise((resolve, reject)=>{
// do your process
reolve() // resolve with updated data
})
pythonProcess.then(img =>{
//save to db
})
})
Now In Your case best possible, I prefer is to use aync/await Async/await MDN. Do not take it as another way to do it, It is just modern way to use promise. As internally await also setup promise chain. You have both options, either you can go it through promises you will get handy to the one of the best thing of javascript or If you want small piece of code go with await.
router.post('/img2text', upload.single('photo'), async(req, res) => { // To use await function need to be a async function
// Create the object for insertion into database
const Img2text = new img2text({
imgPath: req.file.path,
sender: req.body.sender,
processed: 0,
resultText: "no result",
});
/////////////////////////////////////////////////////////////start
var exec = require("child_process").exec;
await exec(`python uploads/hello.py ${req.file.path}`,(error, stdout, stderr) => {
if (error) {
Img2text.processed=0;
Img2text.resultText='no result'
console.error(`exec error: ${error}`);
return;
}
console.log(`stdout: ${stdout}`);
Img2text.processed=1;
Img2text.resultText=stdout.toString('utf8');
console.log(`stderr: ${stderr}`);
console.log(req.query.firstname);
});
/////////////////////////////////////////////////////////end
// Save into database
Img2text.save((err) => {
// Check if error
if (err) {
res.json({ success: false, message: err });
} else {
console.log("img saved !!")
res.json({ success: true, message:img.resultText }); // Return success message
}
});
});

Categories