How to make flask application run every 5 minutes?

Viewed 1947

I have this application that works how I want to but now I want it to grab live data from some virtual machines every 5 minutes. In the code below I have it set to renew every ten seconds just to see if it works but nothing is happening. I am using time.sleep. What else am I missing?

import time
from flask import Flask, render_template
from testapi import grab_cpu

app = Flask(__name__)

starttime = time.time()
while True:

    machines =["build05", "build06", "build07","build08", "build09", "build10", "build11", "build12","build14","build15", "winbuild10","winbuild11", "winbuild12", "winbuild13", "wbuild14", "wbuild15", "winbuild16", "winbuild17", "winbuild18"] 

    cpu_percentage =[grab_cpu("build05"), grab_cpu("build06"),grab_cpu("build07"),  
            grab_cpu("build08"), grab_cpu("build09"), grab_cpu("build10"), grab_cpu("build11"), grab_cpu("build12"), grab_cpu("build13"), grab_cpu("build14"), grab_cpu("build15"), grab_cpu("winbuild10"), grab_cpu("winbuild11"), grab_cpu("winbuild12"), grab_cpu("winbuild14"), grab_cpu("winbuild15"), grab_cpu("winbuild16"), grab_cpu("winbuild17"), grab_cpu("winbuild18")] 

    @app.route("/")  # this sets the route to this page
    def home():
        return render_template('testdoc.html', len = len(machines), machines = machines, cpu_percentage = cpu_percentage)

    app.run(use_reloader = True, debug = True) 
    time.sleep(10.0 - ((time.time() - starttime) % 10.0))

Edit (this is an update with the suggestions, it's still not working as i'd like): Edit 2 more info: I have one file with a function, grab_cpu, that does an api call to a vm and returns the percentage of usage. I have another file called test doc.html which just displays the html. From these responses i'm guessing I need to use some javascript and something with sockets. Can someone please drop a link to point me in the right direction?

import time
from flask import Flask, render_template
from testapi import grab_cpu

app = Flask(__name__)



@app.route("/")  # this sets the route to this page
def home():
    starttime = time.time()

    while True:
        machines =["build05", "build06", "build07","build08", "build09", "build10", "build11", "build12","build14","build15", "winbuild10","winbuild11", "winbuild12", "winbuild13", "wbuild14", "wbuild15", "winbuild16", "winbuild17", "winbuild18"] 

        cpu_percentage =[grab_cpu("build05"), grab_cpu("build06"),grab_cpu("build07"),  
            grab_cpu("build08"), grab_cpu("build09"), grab_cpu("build10"), grab_cpu("build11"), grab_cpu("build12"), grab_cpu("build13"), grab_cpu("build14"), grab_cpu("build15"), grab_cpu("winbuild10"), grab_cpu("winbuild11"), grab_cpu("winbuild12"), grab_cpu("winbuild14"), grab_cpu("winbuild15"), grab_cpu("winbuild16"), grab_cpu("winbuild17"), grab_cpu("winbuild18")] 
        return render_template('testdoc.html', len = len(machines), machines = machines, cpu_percentage = cpu_percentage)
        time.sleep(10.0 - ((time.time() - starttime) % 10.0))
app.run(use_reloader = True, debug = True) 
    

Here is the page: picture of site

Thank you.

2 Answers

I recommend against using Flask to handle the scheduling. The intended flow for Flask requests is:

  1. Receive an HTTP request from the browser
  2. Generate a response as quickly as possible (ideally in a few milliseconds, but almost always less than a few seconds)
  3. Return the response to the browser

The intention of the code above seems to be to use Flask to push updates down to the browser, but Flask can only respond to incoming requests, not force the browser to change.

For the use case you're describing, a simpler solution would be to handle the refresh logic in the browser.

For a very rudimentary example, put this into your testdoc.html template:

<script type="text/javascript">
    setTimeout(function () { 
      location.reload();
    }, 10 * 1000);
</script>

That will reload the page every 10 seconds, which will generate a new request to your Flask server and display updated information in your browser.

If you want to get fancier and avoid reloading the entire page, you can use JavaScript XmlHttpRequests or the more modern Fetch API to update specific elements of the page asynchronously.

Also, here's a suggested simplification of the Python code:

from flask import Flask, render_template
from testapi import grab_cpu

app = Flask(__name__)

build_machines = list(map(lambda i: 'build%02d' % i, range(5, 16)))
win_build_machines = list(map(lambda i: 'winbuild%02d' % i, range(10, 19)))
machines = build_machines + win_build_machines

# Handle HTTP GET requests to / route
@app.route("/", methods=['GET'])
def home():
        cpu_percentage = list(map(lambda b: grab_cpu(b), machines))
        return render_template('testdoc.html', len = len(machines), machines = machines, cpu_percentage = cpu_percentage)

app.run(use_reloader = True, debug = True) 
    

Your code needs some work, Flask views are not meant to be declared inside a loop. I would suggest the following:

  1. Remove the Flask view from inside the while loop.
  2. Declare the server outside the loop too.
  3. Write your code inside a function.
  4. Run the while loop just calling the function to grab the information from your sources.

Based on what I think is just an example made on the run, I will also make some assumptions about your requirement:

  • This is not exactly your code.
  • Your code works perfectly, but this implementation is lacking.
  • Your project has some level of complexity.
  • You need to show this data somewhere else.

Base on these (and perhaps additional conditions), I would say you have two ways to achieve in an effective way what you need:

  1. (Not recommended) Create a Cron Job and run everything in a dedicated script.
  2. (My favorite) Encapsulate the logic of your script inside a Flask API call, a method, or a function, and declare it as a Celery task, scheduled to run every 5 seconds, updating your database, and use a view with some JS reactivity to show the data in realtime.
Related