วันเสาร์ที่ 31 สิงหาคม พ.ศ. 2556

Display Progress for Long Process

Situation

- upload file from a HTML form with submit button
- after submit, processing file take too long and timeout occur
- want to display progress after submit request

Solution 1

- use thread to process request, thus return response immediately
- keep progress data in session
- use Ajax to submit and display progress
Drawback
- no progress shown during submit big file

Solution 2

- use submit button to submit request
- use thread to process request, thus return response immediately
- keep progress data in session
- redirect to progress page, which use Ajax to display progress

Detail for Solution : use thread to process request

- for uwsgi, enable thread in uwsgi.ini by adding this row
    # enable thread
    enable-threads=True
- in Django, use code below to create thread
    import threading
    ...

    def func():
        ...
        args = [arg1, arg2]
        t = threading.Thread(target=do_something, args=args)
        t.daemon = True
        t.start()

    def do_something(arg1, arg2):
        ...


Detail for Solution : use Ajax to submit form data

- use jQuery Form Plugin to submit form data (especially uploaded file) from ajax
  detail here >> http://jquery.malsup.com/form/#getting-started
  detail for available options >> http://jquery.malsup.com/form/#options-object
    options = {
        type: "POST",
        url: "/import_ajax", // target url to submit form data
        validate: true,
        data: {'validate_question_ajax': '1'}, // additional data to be submitted with other form data
        dataType: 'json',
        success: function(response, textStatus, jqXHR){
            ...
            // start timer (to display progress) only if request is submitted successfully             init_timer();
        },
        // callback handler that will be called on error

        error: function(jqXHR, textStatus, errorThrown){
            ...
        }
    };
    $('#MyForm').ajaxSubmit(options);

Detail for Solution : save progress in session

- code below in django to get/set progress in session
    // enforce no cache for progress
    // redirect to this method from url, e.g. "/import_progress"

    @cache_control(no_cache=True)
    def get_progress(request):
        // use session key in request to get session data

        s = SessionStore(session_key=request.session._session_key)
        progress = s.get('progress', None)
        is_done = s.get('is_done', None)
        if is_done:
            return JSONResponse({'message': 'done %s records.' % progress, 'detail': s['detail']})
        else:
            return JSONResponse({'message': 'process %s records.' % progress, })

    def set_progress(session_key, progress, detail=None, reset=False):
        s = SessionStore(session_key=session_key)
        s['progress'] = progress
        s['is_done'] = (detail is not None)
        if detail:
            s['detail'] = detail
        elif reset:
            s['detail'] = []
        s.save()

    def do_something(request):
        // reset progress data immediately before process request

        set_progress(request.session._session_key, 0, reset=True)

Detail for Solution : use Ajax to display progress

- code below in javascript
    var progress_timer;

    // a function to initialize timer (called later)

    function init_timer() {
        // set timer to update status of importing Slam history

        progress_timer = setInterval(function() {
            $.ajax({
                url: "/import_progress", // url that return progress data, redirect to method get_progress() above
                ...
                success: function(response, textStatus, jqXHR){
                    // output progress

                    $("#import_progress").html(response.message);
                    if (response.is_done != null) {
                        // stop timer when process completed

                        clearInterval(progress_timer);
                    }
                    // emergency braker

                    i += 1;
                    if (i > 1000) {
                        clearInterval(progress_timer);
                    }
                },
                ...
            });
        }, 5000); // timer interval in millisec
    }

    ...

        // pass this as ajax submit option
        // this will be returned immediately after worker thread started

        success: function(response, textStatus, jqXHR){
            if (response.error != null) {
                ...
            }
            else {
                // if worker thread started normally, start timer to display progress
                init_timer();
            }
        },

Detail Solution : HTML page to show progress

- code below in javascript to initialize timer when loaded page
    $(function() {
        jQuery(document).ready(function(e) {
            init_timer(); // same function as above
        });
    });

ไม่มีความคิดเห็น:

แสดงความคิดเห็น