Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create a hyperlink (or button) that executes a python script and then redirects when script completes

Alright, I managed to get it working how I want (although some/most might disagree with the method). I used Flask as recommended below. The part that might be considered "wrong" is the 404 check loop. It isn't proper design and results in a "stuck script" error in some browsers if it goes on for too long. However, the script I want to run doesn't last long enough for that to be a problem.

Thanks for the help and please let me know if you have any other suggestions.

Flask App:

import threading
import subprocess
import os
import sys
from flask import Flask
from flask import render_template, abort
app = Flask(__name__)
app.debug = True

def run_script():
    theproc = subprocess.Popen([sys.executable, "run_me.py"])
    theproc.communicate()

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

@app.route('/generate')
def generate():
    threading.Thread(target=lambda: run_script()).start()
    return render_template('processing.html')

@app.route('/is_done')
def is_done():
    hfile = "templates\\itworked.html"
    if os.path.isfile(hfile):
        return render_template('itworked.html')
    else:
        abort(404)

if __name__ == "__main__":
    app.run()

processing.html:

<!DOCTYPE html>
<html>
<head>
    <script src="/static/jquery.min.js"></script>
</head>

<body>
<p>Processing...</p>
<script>
    setInterval(function()
    {
        var http = new XMLHttpRequest();
        http.open('HEAD', "is_done", false);
        http.send();
        if (http.status==200)
            window.location.replace("is_done");
    },2000);
</script>
</body>
</html>

Original question:

I am a beginner/intermediate Python programmer and a beginning web developer so please be gentle. What I want: A user clicks a hyperlink and is taken to an intermediate web page that says something like "Processing...". In the background (on the server) this link triggers a python script that processes a file and creates a new web page. On completion of the script, the user is redirected to the newly created page. What I have: I have a script that does the processing and spits out a new page. What I haven't figured out is how to trigger the python script and then trigger the redirect on script completion. I've looked at web frameworks like django, and cgi scripting. But I haven't found anything I feel fits the bill. It's very possible I'm just missing something completely obvious so I'd really appreciate any help. Thanks.

like image 434
bem3ry Avatar asked Jun 05 '12 19:06

bem3ry


1 Answers

A simple way to tackle this problem would be to use a simple web framework like Flask to build the web part uf your system. In the request handler for your magic link, you would need to spawn your script and keep track of it. A simple way to see if your script is done and relay that to your user is to periodically send off an ajax request to check for completion.

So for example the Flask website could look like:

import threading
import subprocess
import uuid
from flask import Flask
from flask import render_template, url_for, abort, jsonify, request
app = Flask(__name__)

background_scripts = {}

def run_script(id):
    subprocess.call(["/path/to/yourscript.py", "argument1", "argument2"])
    background_scripts[id] = True

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

@app.route('/generate')
def generate():
    id = str(uuid.uuid4())
    background_scripts[id] = False
    threading.Thread(target=lambda: run_script(id)).start()
    return render_template('processing.html', id=id)

@app.route('/is_done')
def is_done():
    id = request.args.get('id', None)
    if id not in background_scripts:
        abort(404)
    return jsonify(done=background_scripts[id])

And the index.html:

<a href="{{ url_for('generate') }}">click me</a>

And processing.html:

<html>
<head>
<script src="/static/jquery.js"></script>
<script>
    function ajaxCallback(data) {
        if (data.done)
            window.location.replace("http://YOUR_GENERATED_PAGE_URL");
        else
            window.setTimeout(function() {
                $.getJSON('{{ url_for('is_done') }}', {id: {{ id }} }, ajaxCallback);
            }, 3000);
    }
    $(document).ready(function(){
        ajaxCallback({done=false});
    });
</script>
</head>
<body>
    Processing...
</body></html>

This is all untested code at the moment, but I hope you get some idea on how to approach this problem. Also keep in mind that this will only work if you serve the page from one process, so if you set up Apache and mod_wsgi, make sure there is only one process in the process group.

If you need a more complex solution, you might want to look at message queues and the like.

like image 132
mensi Avatar answered Sep 19 '22 23:09

mensi