简体   繁体   中英

How to update the HTML page with some updates while flask is performing a task via socketIO

I have a basic flask script that runs a simple ping command via subprocess on an IP entered by a user. however the pings goes on for 3 packets, is there a way to let the user know that the process is ongoing while he waits via socketIO feature on flask?

For now to let the user know that something is happening on the background, i have set setTimeout function to change the button text at set intervals.

The ask is to update this button text with something from the server like "processing... .. Running ping to $IP.. received $NUMBER of packets " via socketIO, is this possible?

from flask import Flask,render_template,request,redirect,session
import subprocess
from flask_socketio import SocketIO
async_mode = None
app = Flask(__name__)
socket_ = SocketIO(app, async_mode=async_mode)


app.secret_key = "sdfwq34qweds"

@app.route('/')
@app.route('/home')
def home():
    return render_template('home.html',sync_mode=socket_.async_mode)

@app.route('/about')
def about():
    return render_template('about.html')

@app.route('/contact')
def contact():
    return render_template('contact.html')

@app.route('/result' ,methods=['POST','GET'])
def result():
    if request.method == "POST":
        ip1=request.form.get('ip')
        ticket=request.form.get('tic')
        starttime=request.form.get('stime')
        endtime=request.form.get('etime')
        session["ip2"]=request.form.get('ip')
        session["ticket2"]=request.form.get('tic')
        session["starttime2"]=request.form.get('stime')
        session["endtime2"]=request.form.get('etime')
        output=subprocess.Popen(["/usr/bin/python3.8 /u0/sn/dev_scripts/process.py -i "+str(ip1)+" -t "+str(ticket)+" -f "+str(starttime)+" -e "+(endtime)],shell=True,stdout=subprocess.PIPE)
        output.wait()
        out=output.communicate()[0]
        ec=output.returncode
        return render_template('result.html',sync_mode=socket_.async_mode,out=out.decode())

if __name__ == '__main__':
    socket_.run(app,host='0.0.0.0',port=8080,debug=True)

my home.html page

root@prod-nexus-app01:/u0/sn/dev_nexus# cat templates/home.html
{% extends 'architecture.html' %}
{% block title %}
<title>LaunchPad</title>
{% endblock %}

<body>
  {% block head %}
  <h1 class="display-1" style="text-align: center;">PING TESTER</h1>
  <blockquote class="blockquote">
  </blockquote>
  <figure style="text-align:center;">
    
  </figure>
  {% endblock %}

  {% block content %}
  <form id="nexus"  action="/result"  method="POST">
  <div class="form-floating mb-3 animate__animated animate__fadeInDown">
    <input type="integer"  name="ip" id='ipadd'  placeholder="8.8.8.8"  class="form-control" >
    <label for="ip">Machine IP</label>
  </div>
  <div class="form-group form-floating mb-3 animate__animated animate__fadeInDown">
    <input  class="form-control input-lg" type="datetimepicker"  required="required" placeholder="YYYY/MM/dd-hh:mm" id="fromdatetime"  name="stime" autocomplete="off">
    <label for="stime">Start Time</label>
    <script src="../static/js/flatpickr"></script>
    <script>
        $('#fromdatetime').flatpickr({
       //step:1,
        //format: 'Y/m/d-H:i',
        //maxTime: "now" ,
        //maxDate: "today",
        //minDate: new Date().setDate(new Date().getDate() - 2),
                    //defaultTime: '00:00',
       //altInput:true,
  enableTime: true,
        //showAlways: true,
        time_24hr: true,
        dateFormat: "d/m/Y/H:i",
        allowInput: true,
        //maxDate:"today",
       minuteIncrement:30,
       // minDate:new Date().setDate(new Date().getDate() - 2),
       });
 </script>
</div>

<div class="form-group form-floating mb-3 animate__animated animate__fadeInDown">
    <input  class="form-control input-lg" type="datetimepicker"  required="required" placeholder="YYYY/MM/dd-hh:mm" title="Ending Time-frame"   id="endtime"  name="etime" autocomplete="off">
  <label for="etime">End Time</label>
    <script type='text/JavaScript'>
              $('#endtime').flatpickr({
       //step:1,
        //format: 'Y/m/d-H:i',
        //maxTime: "now" ,
        //maxDate: "today",
        //minDate: new Date().setDate(new Date().getDate() - 2),
                    //defaultTime: '00:00',
        enableTime: true,
        showAlways: true,
        time_24hr: true,
        dateFormat: "d/m/Y/H:i",
        allowInput: true,
        //maxDate:"today",
        minuteIncrement:30,
      //  minDate:new Date().setDate(new Date().getDate() - 2),
       });
     </script>
  </div>
  <input  id= "submit_lead"   onClick = "changeText()"  type="submit" class="btn inputdisabled btn-dark animate__animated animate__fadeInDown" value="Execute ...">
<script>

### FOR NOW THIS WILL JUST UPDATE THE BUTTON TEXT ACCORDING TO TIME ELAPSED ###

 $(document).ready(function(e) {
  $('#nexus').submit(function() {
    var txt = $('#submit_lead');
    txt.val("Fetching logs...");
          setTimeout(function(){txt.val("Taking longer than 60s..")},60000)
    setTimeout(function(){txt.val("100 Secs elapsed, this may take a while ....")},100000)
          setTimeout(function(){txt.val("200s elapsed,This is taking longer than we thought ... ")},200000)
      setTimeout(function(){txt.val("300s elapsed,but we are still on it!")},300000)
      setTimeout(function(){txt.val("400s elapsed,hang tight!... ")},400000)
      setTimeout(function(){txt.val("500s elapsed,Either Slow speeds or large file size!... ")},500000)
      setTimeout(function(){txt.val("600s elapsed,Logs exceeding several Gigs")},600000)
      setTimeout(function(){txt.val("700s elapsed,likely an overloaded GHost")},700000)
      setTimeout(function(){txt.val("800s elapsed...Hold on")},800000)
      setTimeout(function(){txt.val("900s elapsed....")},900000)
  });
});
</script>
    </form>

  {% endblock %}

  </body>

To make updates in real-time, you want two things:

  • a way to get the information from the subprocess incrementally
  • a method of pushing updates to the browser as the subprocess continues on the server

The first requirement can be achieved by opening the subprocess and reading from its stdout with an iterator rather than waiting for the process to complete.

The second requirement can be achieved by a websocket that emits updates from the server and catches them on the browser.

Here's an example that is a bit simpler, but hopefully gives an idea.

Server-side code:

from flask import Flask, render_template
from flask_socketio import SocketIO, emit, disconnect

import subprocess

async_mode = None

app = Flask(__name__)

socket_ = SocketIO(app, async_mode=async_mode)

@app.route('/')
def index():
    return render_template('index.html',
                           sync_mode=socket_.async_mode)

@socket_.on('do_task', namespace='/test')
def run_lengthy_task(data):
    try:
        proc = subprocess.Popen(['ping', '127.0.0.1', '-c', data['count']], bufsize=0, stdout=subprocess.PIPE)
        for line in iter(proc.stdout.readline, b''):
            emit('task_update', { 'data': line.decode('utf-8')[:-1] })
        proc.stdout.close()
        proc.wait()
        result = proc.returncode
        emit('task_done', {'result': result})
        disconnect()
    except Exception as ex:
        print(ex)

if __name__ == '__main__':
    socket_.run(app, host='0.0.0.0', port=80, debug=True)

Client-side code:

<!DOCTYPE HTML>
<html>
<head>
    <title>Long task</title>
    <script src="https://code.jquery.com/jquery-3.6.3.js"></script>
    <script src="https://cdn.socket.io/4.5.4/socket.io.min.js"></script>
    <script type="text/javascript" charset="utf-8">
         $(document).on('click', '.widget input', function (event) {
            namespace = '/test';
            var socket = io(namespace);

            socket.on('connect', function() {
                $('#messages').append('<br/>' + $('<div/>').text('Requesting task to run').html());
                socket.emit('do_task', {count: '10'});
            });
            socket.on('task_update', function(msg, cb) {
                $('#messages').append('<br/>' + $('<div/>').text(msg.data).html());
                if (cb)
                    cb();
            });
            socket.on('task_done', function(msg, cb) {
                $('#messages').append('<br/>Result' + $('<div/>').text(msg.result).html());
                if (cb)
                    cb();
            });
            event.preventDefault();
        });
    </script>
</head>
<body>
    <div class="widget">
        <input type="submit" value="Click me" />
    </div>
    <h3>Messages</h3>
    <div id="messages" ></div>
</body>
</html>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM