Drop in, dependency-free progress bars for your Django/Celery applications.
Super simple setup. Lots of customization available.
Celery Progress Bar demo on SaaS Pegasus
Github demo application: build a download progress bar for DjangoStarting with Celery can be challenging, eeintech built a complete Django demo application along with a step-by-step guide to get you started on building your own progress bar!
If you haven't already, make sure you have properly set up celery in your project.
Then install this library:
pip install celery-progress
First add celery_progress
to your INSTALLED_APPS
in settings.py
.
Then add the following url config to your main urls.py
:
from django.urls import path, include urlpatterns = [ # your project's patterns here ... path(r'^celery-progress/', include('celery_progress.urls')), # add this line (the endpoint is configurable) ]
In your task you should add something like this:
from celery import shared_task from celery_progress.backend import ProgressRecorder import time @shared_task(bind=True) def my_task(self, seconds): progress_recorder = ProgressRecorder(self) result = 0 for i in range(seconds): time.sleep(1) result += i progress_recorder.set_progress(i + 1, seconds) return result
You can add an optional progress description like this:
progress_recorder.set_progress(i + 1, seconds, description='my progress description')
In the view where you call the task you need to get the task ID like so:
views.py
def progress_view(request): result = my_task.delay(10) return render(request, 'display_progress.html', context={'task_id': result.task_id})
Then in the page you want to show the progress bar you just do the following.
Add the following HTML wherever you want your progress bar to appear:display_progress.html
<div class='progress-wrapper'> <div id='progress-bar' class='progress-bar' style="background-color: #68a9ef; width: 0%;"> </div> </div> <div id="progress-bar-message">Waiting for progress to start...</div>Import the javascript file.
display_progress.html
<script src="{% static 'celery_progress/celery_progress.js' %}"></script>Initialize the progress bar:
// vanilla JS version document.addEventListener("DOMContentLoaded", function () { var progressUrl = "{% url 'celery_progress:task_status' task_id %}"; CeleryProgressBar.initProgressBar(progressUrl); });
or
// JQuery $(function () { var progressUrl = "{% url 'celery_progress:task_status' task_id %}"; CeleryProgressBar.initProgressBar(progressUrl) });Displaying the result of a task
If you'd like you can also display the result of your task on the front end.
To do that follow the steps below. Result handling can also be customized.
Initialize the result block:This is all that's needed to render the result on the page.
display_progress.html
<div id="celery-result"></div>
But more likely you will want to customize how the result looks, which can be done as below:
// JQuery var progressUrl = "{% url 'celery_progress:task_status' task_id %}"; function customResult(resultElement, result) { $( resultElement ).append( $('<p>').text('Sum of all seconds is ' + result) ); } $(function () { CeleryProgressBar.initProgressBar(progressUrl, { onResult: customResult, }) });
This library includes experimental support for working with Celery groups. You can use the "group_status"
URL endpoint for this. Here is a basic example:
Example task:
@shared_task(bind=True) def add(self, x, y): return x + y
Calling view:
from celery import group from .tasks import add def progress_view(request): task_group = group(add.s(i, i) for i in range(100)) group_result = task_group.apply_async() # you must explicitly call the save function on the group_result after calling the tasks group_result.save() return render(request, 'display_progress.html', context={'task_id': group_result.id})
Template:
document.addEventListener("DOMContentLoaded", function () { var progressUrl = "{% url 'celery_progress:group_status' task_id %}"; CeleryProgressBar.initProgressBar(progressUrl); });
The initProgressBar
function takes an optional object of options. The following options are supported:
The barColors
option allows you to customize the color of each progress bar state by passing a dictionary of key-value pairs of state: #hexcode
. The defaults are shown below.
The defaultMessages
option allows you to override some default messages in the UI. At the moment these are:
Additionally, this library offers WebSocket support using Django Channels courtesy of EJH2.
A working example project leveraging WebSockets is available here.
To use WebSockets, install with pip install celery-progress[websockets,redis]
or pip install celery-progress[websockets,rabbitmq]
(depending on broker dependencies).
See WebSocketProgressRecorder
and websockets.js
for details.
By default, anyone can see the status and result of any task by accessing /celery-progress/<task_id>
To limit access, you need to wrap get_progress()
in a view of your own which implements the permissions check, and create a new url routing to point to your view. Make sure to remove any existing (unprotected) celery progress urls from your root urlconf at the same time.
For example, requiring login with a class-based view:
# views.py from celery_progress.views import get_progress from django.contrib.auth.mixins import LoginRequiredMixin from django.views.generic import View class TaskStatus(LoginRequiredMixin, View): def get(self, request, task_id, *args, **kwargs): # Other checks could go here return get_progress(request, task_id=task_id)
# urls.py from django.urls import path from . import views urlpatterns = [ ... path('task-status/<uuid:task_id>', views.TaskStatus.as_view(), name='task_status'), ... ]
Requiring login with a function-based view:
# views.py from celery_progress.views import get_progress from django.contrib.auth.decorators import login_required @login_required def task_status(request, task_id): # Other checks could go here return get_progress(request, task_id)
# urls.py from django.urls import path from . import views urlpatterns = [ ... path('task-status/<uuid:task_id>', views.task_status, name='task_status'), ... ]
Any links to 'celery_progress:task_status'
will need to be changed to point to your new endpoint.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4