1

I am using flask in an application that does some long-running computations. I've successfully offloaded the compute-intensive code to an rq worker with this code

job = q.enqueue(run_simulation)

This is followed by the following code:

while not job.is_finished:
    time.sleep(1)
    job.refresh()
    print(job.meta)

the run_simulation code updates its progress using job.meta as follows:

while still_stuff_to_do:
    job = get_current_job()
    job.meta['progress'] = percent_complete
    job.save_meta()
    do_more_stuff...

Everything described so far works as intended. In the console, I get the percent_complete printed out once per second.

The user experience I would like to have is that, once the compute-intensive job is kicked off, I'd like to pop up a modal dialog with a progress bar (using bootstrap) and a cancel button. The progress bar should update the progress indicator once per second. I'd like suggestions for a simple approach to solving this. Specifically, I don't understand what the execution model should be.

mike_thecode
  • 171
  • 2
  • 6
JGV
  • 163
  • 3
  • 14
  • 1
    I've had a shot at implementing the basics of this, using server sent events. See [this gist](https://gist.github.com/vulcan25/52fe1ba5860d0a0448d99fc74428123e) for the full code. There's a video too so you can visualize the result. The code probably needs polished but the basic concept is there, and should be simple enough. Let me know if any questions ;) – v25 Feb 27 '21 at 01:39
  • @v25, thank you for this solution. It works beautifully. I truly appreciate you going "above and beyond" my request and creating the gist!! – JGV Feb 27 '21 at 14:18
  • 1
    No problem, glad this works for you. I'd hinted at something like this in an [earlier answer](https://stackoverflow.com/a/59495587/2052575) so was just waiting for the excuse to build it :) In fact, this post which mentioned the `job.meta` attribute was the turning point, as I'd previously imagined having to manually build that functionality with redis. This is a nice approach which is native to `rq` so thanks for your clear question. – v25 Feb 27 '21 at 14:32
  • @v25, I got 99% of the way though integrating your ideas, but I still have problem. When the background task completes, I trigger my app to render a page with the results of my background task by sending a request to the app using the Fetch API. My route gets executed (I can set a breakpoint and step through my code, verifying everything works correctly). However, when I return render_template(), nothing happens. If I print out the request prior to render template and then click on it, it renders correctly. I suspect there is something wrong with the request that is generated by Fetch. – JGV Mar 03 '21 at 19:12
  • A Fetch request needs a flask endpoint to return a JSON response, rather than a rendered template. Can you create a gist with your exisiting approach, and link back here then perhaps I can help. – v25 Mar 04 '21 at 00:57
  • @V25, Thanks for the offer, but I figured it out. I needed my fetch to render the HTLM that was returned by the fetch(). Basically I did this: fetch(`/scenario/` + job_id + `/display_result`, { method: "GET", }).then((response) => { return response.text(); }) .then((html) => { document.body.innerHTML = html }); – JGV Mar 04 '21 at 13:14
  • @v25, thank you for the offer, but I figured it out. Basically, I needed to render the returned HTML. The solution looks like this: ` fetch(`/scenario/` + job_id + `/display_result`, { method: "GET", }).then((response) => { return response.text(); }) .then((html) => { document.body.innerHTML = html });` – JGV Mar 04 '21 at 13:17

0 Answers0