Availability |
Odoo Online
Odoo.sh
On Premise
|
Lines of code | 1794 |
Technical Name |
web_progress |
License | LGPL-3 |
Website | https://github.com/gmarczynski/odoo-web-progress |
Versions | 11.0 12.0 13.0 14.0 15.0 16.0 17.0 18.0 |
Dynamic Progress Bar
Progress bar for Odoo waiting screen, possibility to cancel an ongoing operation and a system tray menu for all operations in progress.


web_progress is compatible with Odoo 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0 (CE and EE).
Author: Grzegorz Marczyński
License: LGPL-3.
Copyright © 2025 Grzegorz Marczyński
What's New in Version 3.0
- Complete OWL components rewrite for Odoo 17 modern architecture
- Enhanced UI blocking with "Put to background" functionality
Key Features
- Comprehensive Progress Tracking - Built-in progress reporting for all standard Odoo import/export operations
- System Tray Management - Convenient menu showing all ongoing operations with real-time status updates
- Background Operations - Move long-running tasks to background while keeping UI responsive
- UI Blocking Protection - Optional UI blocking with easy background transition (inactive by default in Odoo 17)
- Real-time Updates - Live progress updates via longpolling with automatic fallback
- Operation Cancellation - Cancel long-running operations safely with proper exception handling
- Multiple Styles - Choose from standard, simple, or fun nyan cat progress bars
- Nested Operations - Support for multi-level progress tracking with sub-operations
- Cron Job Support - Works with scheduled tasks and background jobs
- Generator Support - Efficient handling of large datasets and generators
For Developers
Adding progress tracking to your operations is incredibly simple:
def action_operation(self):
for rec in self:
rec.do_something()
# After: Progress-enabled loop
def action_operation(self):
for rec in self.with_progress(msg="Processing records"):
rec.do_something()
Advanced Usage
for item in self.web_progress_iter(data_generator, total=10000):
process_item(item)
# With proper cancellation handling
try:
for rec in self.with_progress(msg="Critical operation"):
rec.perform_task()
except UserError as e:
if "cancelled" in str(e):
self.cleanup_after_cancel()
Parameters available: msg
, total
, cancellable
, log_level
User Experience
- Intuitive Interface - Progress bars appear automatically for long-running operations
- Flexible Viewing - Switch between modal dialog and system tray views
- Smart Notifications - Clear status messages including "No ongoing operations" when idle
- Responsive Design - Works seamlessly on desktop and mobile interfaces
- Non-intrusive - Minimal performance impact on your operations
- Reliable - Automatic cleanup and error recovery
Technical Highlights
- Separate Transactions - Progress tracking doesn't interfere with your main operations
- Longpolling Integration - Real-time updates using Odoo's built-in messaging system
- Memory Efficient - Generator-based iteration with no memory overhead
- Thread Safe - Multiple operations can run simultaneously without conflicts
- Automatic Cleanup - Progress records are automatically removed after completion
Installation & Compatibility
Simply install the module and start using with_progress()
or web_progress_iter()
in your code. No additional configuration required!
Requirements: Longpolling should be operational for real-time updates (falls back to periodic polling if unavailable).
Adding progress tracking to your code
Prerequisites
Progress reporting uses longpolling to send progress data from backend to web client, so make sure that the longpolling is operational before testing this module.
Simple case
Typically when your code executes any long-running operation there is a loop over a collection in your code.
In order to report progress of the operation, wrap the collection with self.web_progress_iter(collection, msg="Message")
Say, your operation's main method looks as follows:
def action_operation(self): for rec in self: rec.do_somethig()
Then a progress-reporting-ready version would be:
def action_operation(self): for rec in self.web_progress_iter(self, msg="Message"): rec.do_something()
or a simpler version for recordsets:
def action_operation(self): for rec in self.with_progress(msg="Message"): rec.do_something()
Progress tracking may be added to sub-operations as well:
def action_operation(self): for rec in self.with_progress(msg="Message"): lines = rec.get_lines() for line in lines.with_progress("Sub-operation") line.do_something()
Advanced case
This module adds methods web_progress_iter and with_progress to every Odoo model. The only difference between these methods is that web_progress_iter requires as its first parameter a collection to iterate upon and with_progress iterates always on self.
Both methods accept the following optional parameters:
- msg (str): an operation description, the message to be shown in the progress report,
- total (int): if provided, will be used as a length of the given collection, so len(collection) will never be called, which is essential when tracking progress of generators (default is None, i.e. len will be called),
- cancellable (bool): whether cancelling the operation should be possible, i.e visible button "Cancel" (default is True),
- log_level (str): which log level shall be used when logging progress (default is "info").
def action_operation(self, data, length): for row in self.web_progress_iter(data, total=length, msg="Message", cancellable=True, log_level="debug"): self.do_something(row)
Another approach
You can also add iteration progress reporting to any recordset by adding progress_iter=True to its context.
FAQ
In this section you will find answers to the common questions concerning progress reporting implemented in web_progress module.
How to report a problem or ask a question?
Please use the issue tracker of our GitHub repository to report problems or ask questions. You will find it here.
How the progress reporting works?
The progress reporting system works through the following technical steps:
- Context injection: The web client automatically injects a unique progress_code (UUID) into the context of every RPC call to the backend.
- Progress tracking: When you use web_progress_iter or with_progress, the module creates a progress record in the database and starts tracking the iteration.
- Real-time updates: Progress updates are sent to the web client through longpolling, allowing real-time display of progress bars.
- User interface: The progress is displayed in a progress bar that can appear in different locations: - As a blocking modal dialog - In the system tray (top-right corner) - Minimized to the system tray on user request
- Cleanup: When the operation completes or is cancelled, the progress record is automatically cleaned up.
Technical implementation: - Progress records are stored in the web.progress model - Each record contains: code (UUID), message, current progress, total, state, timestamps - Longpolling uses the bus system to push updates to connected clients - If longpolling fails, the system falls back to periodic HTTP polling
How each operation is identified?
- Web client injects a unique progress_code (UUID) into the context of every RPC call towards backend.
- Both web_progress_iter and with_progress convert the given collection (or generator) into an instance of a generator-like class that uses a progress_code from context to perform progress tracking while your code iterates upon the collection.
- Sheduled (cron) actions have their progress_code injected into the context by scheduler prior to their execution.
How often the progress is reported?
For each progress_code (i.e. a unique operation) the first interation (the first element) of the collection wrapped with web_progress_iter or with_progress is reported to the web client (via longpolling).
After that, the progress is reported in intervals of minimum 5 seconds (i.e. any access to any wrapped collection more than 5 seconds after the last reported progress is reported).
Also the final iteration (the last element) of the main wrapped collection (on the top-level) is reported.
What is the overhead of progress reporting?
The overhead of progress reporting is minimal and carefully optimized:
Database overhead: - Each progress update requires a single database write operation using a separate transaction - Progress records are automatically cleaned up after completion or cancellation - The module uses efficient SQL queries to minimize database load
Network overhead: - Progress updates are sent via longpolling, which is very efficient for real-time communication - If longpolling fails, the system falls back to periodic polling (every few seconds) - Updates are batched to avoid excessive network traffic
Memory overhead: - Progress tracking uses generator-like iterators, so no additional memory is consumed - The original collection is not duplicated or modified - Sub-progress tracking uses minimal additional memory for recursion depth tracking
Performance impact: - Progress reporting adds negligible CPU overhead to the main operation - The frequency of progress updates automatically adjusts based on operation speed - For very fast operations (< 1 second), progress reporting may not appear at all
How the operation cancelling works?
Operation cancellation works through the following mechanism:
- User action: User clicks the "Cancel" button in the progress dialog or system tray menu
- Database update: The progress record's state is changed to 'cancel' in the database using a separate transaction
- Detection: During each iteration, the progress iterator checks if the operation was cancelled by querying the database
- Exception raising: If cancellation is detected, a UserError-type exception is raised with the message "Operation has been cancelled by [username]"
- Cleanup: The progress record is automatically cleaned up, and the operation terminates
Important notes: - Cancellation can only occur between iterations, not during the processing of individual items - Operations with cancellable=False parameter cannot be cancelled - The cancellation check happens in a separate transaction to ensure immediate detection - Cancelled operations should be handled with appropriate exception handling in your code
How multi-level progress reporting works?
Multi-level (nested) progress reporting creates a hierarchical progress display:
Structure: - The main operation shows the overall progress (e.g., "Processing 100 records") - Sub-operations show detailed progress within each main iteration (e.g., "Processing lines: 50/200") - Multiple nesting levels are supported automatically
Implementation details: - Each nested with_progress() or web_progress_iter() call creates a sub-progress record - Sub-progress records are linked to their parent via the recur_depth field - The UI displays nested progress bars with appropriate indentation - Parent progress advances only when child operations complete
Database structure: - All progress records share the same code (UUID) but have different recur_depth values - Depth 0 = main operation, depth 1 = first level sub-operation, etc. - Sub-progress records are automatically cleaned up with their parent
Visual representation: - Main progress bar shows overall completion - Sub-progress bars appear indented below the main bar - Each level can have its own message, cancellation settings, and timing
Example behavior: ``` Main operation: 3/10 (30%)
└─ Sub-operation: 150/200 (75%)
Is it possible to put an ongoing operation into background?
Yes, there are two ways to put operations into background:
- "Put to background" button: Click this button in the progress dialog to minimize the operation to the system tray while keeping the UI responsive.
- F5 key: Press F5 (standard Odoo behavior) to put any long-running operation into background.
The difference is that with the "Put to background" button, the progress tracking continues to be visible in the system tray menu, where users can: - Monitor the progress of background operations - Cancel operations if they are cancellable - View multiple background operations simultaneously
Important considerations: - Background operations cannot interact with the user after completion - This is suitable for data imports (unless there are import errors) - This is not suitable for data exports or reports that require user download after completion - When there are no background operations, the system tray shows "No ongoing operations"
Is the current transaction commited to make progress visible?
No. Progress reporting uses a fresh transaction for each progress report and cancelled operation verification; therefore, the main transaction stays untouched and in total isolation.
However, it should be noted that since progress report records and longpolling messages are commited into the database, even if the main transaction is still not commited, the main transaction shall never inspect or change those records in order to avoid inter-transactional conflicts (update-in-parallel exceptions).
Does progress reporting work with reports?
Yes, you can iterate over the wrapped collections in QWeb reports and the progress will be visible to the user.
Please log in to comment on this module