[Welcome] [TitleIndex] [WordIndex

Problem

From a Quixote handler, you want to start some lengthy computational task, but don't want to make the user's browser wait while the task is performed.

Solution

Note: this solution is written for Quixote 1.x. The basic idea should still work for Quixote 2, depending on the server technology used.

XXX write this

# This doesn't work if you use cgi.
import datetime, threading, time

_q_exports = ['job']

def _q_index[html](request):
    header(request, 'long')
    '<a href="job">Start job</a>'
    footer(request)

def header[html](request, header, meta=''):
    '''<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
          "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
    %s
    <title>%s</title>
  </head>
  <body>
''' % (meta, header)

def footer[html](request):
    '\n  </body>\n</html>'

class Thread:
    def __init__(self):
        self.thread = None
        self.status = []

    def add_status(self, s):
        self.status.append((datetime.datetime.now(), s))

thread = None
semaphore = threading.Semaphore()

def do_job():
    thread.add_status('Beginning job.')
    thread.add_status('Feeling tired, sleeping 20 seconds.')
    time.sleep(20)
    thread.add_status('Waking up.')
    thread.add_status('Tired again, sleeping 20.')
    time.sleep(20)
    thread.add_status('Waking up.')
    thread.add_status("Now I'm done.")

def start_job():
    global thread
    semaphore.acquire()
    try:
        if not thread:
            # Start the thread
            thread = Thread()
            thread.thread = threading.Thread(target=do_job)
            thread.thread.start()
    finally:
        semaphore.release()

def get_status():
    status = []
    running = False
    semaphore.acquire()
    try:
        if thread:
            status = thread.status[:]
            if thread.thread and thread.thread.isAlive():
                running = True
    finally:
        semaphore.release()
    return running, status

def job[html](request):
    start_job()
    running, status = get_status()
    if running:
        #request.response.set_header('Refresh', '5; URL=.')
        header(request, 'Job', '<meta http-equiv="Refresh" content="5; URL=%s">' % \
               request.get_url())
        '<h1>Job status</h3>\n'
    else:
        header(request, 'Job')
        '<h3>Last job log</h3>\n'
    for date, msg in status:
        '%s %s<br />\n' % (date.isoformat(), msg)
    footer(request)

Discussion


CategoryCookbook


2010-09-22 22:14