1

I need your help, In my view i run a bash process that takes time depending on the size of the image; while it process i want to display a loading gif image and a sentence (such as "Please wait, the image is being processed"). I tried to did that with a template but it is rendered at the end of execution of the script not while it process. Can someone help me to do that ? I've found a similar question "https://stackoverflow.com/questions/8317219/django-show-loading-message-during-long-processing" but the answer wasn't very clear for me because i never used ajax.

This is my view :

def process(request):
    var = Image.objects.order_by('id').last()
    subprocess.call("./step1.sh %s" % (str(var)), shell=True)
    subprocess.call("./step2.sh %s" % (str(var)), shell=True)
    return render(request, 'endexecut.html')

Template that will be displayed at the end of processing: "endexecut.html"

 {% extends 'base.html' %}
    {% block content %}
    <div class="container">
       <div class="row">
         <div class="jumbotron">
             <div class="row">
                 <center>
                   <p> Your image is processed succesfully ! </p>
                 </center>
              </div>
        </div>
    </div>
</div>
        

     {% endblock %}
Master Irfan Elahee
  • 165
  • 1
  • 1
  • 14
c.ceti
  • 183
  • 2
  • 5
  • 12
  • There will be no way around ajax if you want your webapp to behave like this. So you should get familiar with that technique. – Jingo Jul 12 '17 at 08:19

3 Answers3

2

You should have a look at Celery which allows you to create jobs and monitor them. This would replace your process(request). With your own process(request) you would have to implement that by yourself, and why do that if solutions for it exist already.

The state of your running subprocess can be queried like this using JS from your browser (AJAX):

def task_progress_json(request, job_id):
    """
    A view to report the progress to the user
    :param job_id:
    :param request:
    """
    job = AsyncResult(job_id)
    data = job.result or job.state
    if job.state == states.FAILURE or isinstance(data, Exception):
        data = {'failure': "{}: {}".format(_('Error'), data)}
        if job_id in request.session:
            request.session[job_id] = None
    else:
        # see http://docs.celeryproject.org/en/latest/reference/celery.states.html#std:state-PROPAGATE_STATES
        # and http://docs.celeryproject.org/en/latest/getting-started/next-steps.html#calling-tasks
        if data == states.PENDING:
            last_state = request.session.get(job_id, None)
            if last_state == states.PENDING:
                data = {'failure': "Error: No Job Running"}
                request.session[job_id] = None
            else:
                request.session[job_id] = data
        if isinstance(data, dict):
            next_url = data.pop('next_url', None)
            next_kwargs = data.pop('next_kwargs', {})
            if next_url:
                data['next_url'] = reverse(next_url, kwargs=next_kwargs)
    return HttpResponse(json.dumps(data), content_type='application/json')

In the view that triggers the process, for example when the user clicks on the Submit button of a form, you start the job:

def form_valid(self, form):
    # this is just an example, implement as you like
    fs = FileSystemStorage(location=settings.IMAGE_ROOT)
    stored_file = fs.save(form.cleaned_data['file'].name, form.cleaned_data['file'])
    job = import_imag_task.delay(stored_file, self.request.user.id, self.spectre.id)
    # this is another view that shows the progress bar
    # the process of the progress bar is displayed according to
    # results fetched from the above task_progress_json request via AJAX
    return HttpResponseRedirect(reverse('import_image_progress') + '?job=' + job.id)
Risadinha
  • 16,058
  • 2
  • 88
  • 91
1

Your best shot is to use ajax. You can simply rewrite your view as:

 def process(request):
     return render(request, "endexecut.html")

Display the gif image in your endexecut template by default with whatever text you want to use.

In the script section of the template page, you can then have an ajax call like this:

 $.ajax({
        url: '/some-app/some-view/',
        type: 'GET',
        dataType: 'json',
        success: function(result){
                $('#image-id').attr('src', result.image);
        },
        error: function(xhr){
               alert(xhr.responseText); //Remove this when all is fine.
        }
 });

Ajax is pointing to a url (Which you should have defined in your url.py), the url would point to the view that would do your long bash processing:

  def bash_processing(request):
       # Do your bash processing here
       return JsonResponse({'image': 'image url here'})

That should work fine for you. However, if the processing would take a long time, you may consider using celery to do the processing in background.

yusuf.oguntola
  • 492
  • 3
  • 10
  • Thank you for your answer, do i need to add js syntax to the ajax section you mentionned in my javascript block or just use it between ? – c.ceti Jul 12 '17 at 09:11
  • Just use it between the script section. Additionally, I made a mistake in that code and I've corrected it. The `dataType` in the ajax code should be ``json`` and not ``ajax`` – yusuf.oguntola Jul 12 '17 at 09:12
  • It didn't work, i have the same problem, it process without showing anything and at the end it just shows a json response: image: "https://upload.wikimedia.org/wikipedia/commons/b/b1/Loading_icon.gif" – c.ceti Jul 12 '17 at 09:32
  • 1. The script is loading before the page renders completely. Consider adding the script before `

    ` tag and probably use the `window.onload` to make sure the script waits till everything has rendered. That way, you'd have the default gif image displayed and your ajax would work in the background. If ajax returns and the image is not loaded, that's a bug in your implementation. You may want to show some of the codes you've added in here.

    – yusuf.oguntola Jul 12 '17 at 09:38
  • You will found here my views.py and the template for loading "https://drive.google.com/open?id=0B7K9jTMbrhwIQVBqc2FoWjRVOTQ" and in my urls.py i have : url(r'^wmark/', views.getor, name='getor'), I did all what you said – c.ceti Jul 12 '17 at 12:47
  • 1. Download the gif image, save it into your static folder and load it from there, it should make the image come up immediately. 2. You are returning the same gif image from your view. Is that what you really want? 3. I called `$('#image-id')` from ajax but you did not add an id to your image tag. Add `id="image-id"` to your `` tag. – yusuf.oguntola Jul 12 '17 at 14:27
  • I did all what you said and it didn't work, (the script took about 2, 3 minutes to process, I don't know if that is the problem – c.ceti Jul 12 '17 at 14:49
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/149026/discussion-between-yusuf-oguntola-and-c-ceti). – yusuf.oguntola Jul 12 '17 at 15:49
0

I had the same issues and used jquery and an ajaxcall. This way you can display the gif when you launch the upload and remove the gif when it's done.

Jbertrand
  • 419
  • 1
  • 4
  • 14
  • Do you still have your code or an tutorial that would help me please i'm stuck on this. – c.ceti Jul 12 '17 at 08:41
  • https://stackoverflow.com/questions/13465711/how-do-i-post-with-jquery-ajax-in-django In this one you will see how it works. 1 - Display the gif, hide the rest 2- Ajax call to process your file 3- ajax success function that hide the gif and display the rest – Jbertrand Jul 12 '17 at 08:43