I need a HTML webpage in my Django app to load and show the continous output of a script in a scrollable box. Is this possible?
I'm presently using a subprocess to run a Python script, but the HTML page won't load until after the script has finished (which can take about 5 minutes). I want the users to see something is happening, rather than just a spinning circle.
What I have already also unloads the full output of the script with "\n" in the text; I'd like it to output each new line instead, if possible.
My code is as follows:
Views.py:
def projectprogress(request):
GenerateProjectConfig(request)
home = os.getcwd()
project_id = request.session['projectname']
staging_folder = home + "/staging/" + project_id + "/"
output = ""
os.chdir(staging_folder)
script = home + '/webscripts/terraformdeploy.py'
try:
output = subprocess.check_output(['python', script], shell=True)
except subprocess.CalledProcessError:
exit_code, error_msg = output.returncode, output.output
os.chdir(home)
return render(request, 'projectprogress.html', locals())
projectprogress.html:
<style>
div.ex1 {
background-color: black;
width: 900px;
height: 500px;
overflow: scroll;
margin: 50px;
}
</style>
<body style="background-color: #565c60; font-family: Georgia, 'Times New Roman', Times, serif; color: white; margin:0"></body>
<div class="ex1">
{% if output %}<h3>{{ output }}</h3>{% endif %}
{% if exit_code %}<h3> The command returned an error: {{ error_msg }}</h3>{% endif %}
</div>
<div class="container">
<a class="button button--wide button--white" href="home.html" title="Home" style="color: white; margin: 60px;">
<span class="button__inner">
Home
</span>
</a>
</div>
</body>
</html>
You could simplify your task using StreamingHttpResponse and Popen:
def test_iterator():
from subprocess import Popen, PIPE, CalledProcessError
with Popen(['ping', 'localhost'], stdout=PIPE, bufsize=1, universal_newlines=True) as p:
for line in p.stdout:
yield(line + '<br>') # process line here
if p.returncode != 0:
raise CalledProcessError(p.returncode, p.args)
def busy_view(request):
from django.http import StreamingHttpResponse
return StreamingHttpResponse(test_iterator())
StreamingHttpResponse
expects an iterator as its parameter. An iterator function is one which has a yield
expression (or a generator expression), and its return value is a generator object (an iterator).
In this example, I simply echo the ping command to prove it works.
Substitute ['ping', 'localhost']
by a list (it has to be a list if you pass parameters to the command - in this case, localhost
). Your original ['python', script]
should work.
If you want to know more about generators, I would recommend Trey Hunner's talk, and also strongly that you read chapter 14 of Fluent Python book. Both are amazing sources.
Disclaimer:
Performance considerations
Django is designed for short-lived requests. Streaming responses will tie a worker process for the entire duration of the response. This may result in poor performance.
Generally speaking, you should perform expensive tasks outside of the request-response cycle, rather than resorting to a streamed response.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With