mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 21:25:42 +00:00 
			
		
		
		
	Switch to registry for tasks (#3790)
* Add general registering functions Fixes #3787 * switch over to new method * move to static type * fix unclear naming
This commit is contained in:
		| @@ -54,68 +54,16 @@ class InvenTreeConfig(AppConfig): | |||||||
|  |  | ||||||
|     def start_background_tasks(self): |     def start_background_tasks(self): | ||||||
|         """Start all background tests for InvenTree.""" |         """Start all background tests for InvenTree.""" | ||||||
|         try: |  | ||||||
|             from django_q.models import Schedule |  | ||||||
|         except AppRegistryNotReady:  # pragma: no cover |  | ||||||
|             logger.warning("Cannot start background tasks - app registry not ready") |  | ||||||
|             return |  | ||||||
|  |  | ||||||
|         logger.info("Starting background tasks...") |         logger.info("Starting background tasks...") | ||||||
|  |         # Run through registered tasks | ||||||
|         # Remove successful task results from the database |         for task in InvenTree.tasks.tasks.task_list: | ||||||
|             InvenTree.tasks.schedule_task( |             InvenTree.tasks.schedule_task( | ||||||
|             'InvenTree.tasks.delete_successful_tasks', |                 task.func, | ||||||
|             schedule_type=Schedule.DAILY, |                 schedule_type=task.interval, | ||||||
|         ) |                 minutes=task.minutes, | ||||||
|  |  | ||||||
|         # Check for InvenTree updates |  | ||||||
|         InvenTree.tasks.schedule_task( |  | ||||||
|             'InvenTree.tasks.check_for_updates', |  | ||||||
|             schedule_type=Schedule.DAILY |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|         # Heartbeat to let the server know the background worker is running |  | ||||||
|         InvenTree.tasks.schedule_task( |  | ||||||
|             'InvenTree.tasks.heartbeat', |  | ||||||
|             schedule_type=Schedule.MINUTES, |  | ||||||
|             minutes=15 |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|         # Keep exchange rates up to date |  | ||||||
|         InvenTree.tasks.schedule_task( |  | ||||||
|             'InvenTree.tasks.update_exchange_rates', |  | ||||||
|             schedule_type=Schedule.DAILY, |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|         # Delete old error messages |  | ||||||
|         InvenTree.tasks.schedule_task( |  | ||||||
|             'InvenTree.tasks.delete_old_error_logs', |  | ||||||
|             schedule_type=Schedule.DAILY, |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|         # Delete old notification records |  | ||||||
|         InvenTree.tasks.schedule_task( |  | ||||||
|             'common.tasks.delete_old_notifications', |  | ||||||
|             schedule_type=Schedule.DAILY, |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|         # Check for overdue purchase orders |  | ||||||
|         InvenTree.tasks.schedule_task( |  | ||||||
|             'order.tasks.check_overdue_purchase_orders', |  | ||||||
|             schedule_type=Schedule.DAILY |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|         # Check for overdue sales orders |  | ||||||
|         InvenTree.tasks.schedule_task( |  | ||||||
|             'order.tasks.check_overdue_sales_orders', |  | ||||||
|             schedule_type=Schedule.DAILY, |  | ||||||
|         ) |  | ||||||
|  |  | ||||||
|         # Check for overdue build orders |  | ||||||
|         InvenTree.tasks.schedule_task( |  | ||||||
|             'build.tasks.check_overdue_build_orders', |  | ||||||
|             schedule_type=Schedule.DAILY |  | ||||||
|             ) |             ) | ||||||
|  |         logger.info("Started background tasks...") | ||||||
|  |  | ||||||
|         # Make regular backups |         # Make regular backups | ||||||
|         InvenTree.tasks.schedule_task( |         InvenTree.tasks.schedule_task( | ||||||
|   | |||||||
| @@ -4,7 +4,9 @@ import json | |||||||
| import logging | import logging | ||||||
| import re | import re | ||||||
| import warnings | import warnings | ||||||
|  | from dataclasses import dataclass | ||||||
| from datetime import timedelta | from datetime import timedelta | ||||||
|  | from typing import Callable | ||||||
|  |  | ||||||
| from django.conf import settings | from django.conf import settings | ||||||
| from django.core import mail as django_mail | from django.core import mail as django_mail | ||||||
| @@ -126,6 +128,69 @@ def offload_task(taskname, *args, force_async=False, force_sync=False, **kwargs) | |||||||
|         _func(*args, **kwargs) |         _func(*args, **kwargs) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @dataclass() | ||||||
|  | class ScheduledTask: | ||||||
|  |     """A scheduled task. | ||||||
|  |  | ||||||
|  |     - interval: The interval at which the task should be run | ||||||
|  |     - minutes: The number of minutes between task runs | ||||||
|  |     - func: The function to be run | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     func: Callable | ||||||
|  |     interval: str | ||||||
|  |     minutes: int = None | ||||||
|  |  | ||||||
|  |     MINUTES = "I" | ||||||
|  |     HOURLY = "H" | ||||||
|  |     DAILY = "D" | ||||||
|  |     WEEKLY = "W" | ||||||
|  |     MONTHLY = "M" | ||||||
|  |     QUARTERLY = "Q" | ||||||
|  |     YEARLY = "Y" | ||||||
|  |     TYPE = [MINUTES, HOURLY, DAILY, WEEKLY, MONTHLY, QUARTERLY, YEARLY] | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class TaskRegister: | ||||||
|  |     """Registery for periodicall tasks.""" | ||||||
|  |     task_list: list[ScheduledTask] = [] | ||||||
|  |  | ||||||
|  |     def register(self, task, schedule, minutes: int = None): | ||||||
|  |         """Register a task with the que.""" | ||||||
|  |         self.task_list.append(ScheduledTask(task, schedule, minutes)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | tasks = TaskRegister() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def scheduled_task(interval: str, minutes: int = None): | ||||||
|  |     """Register the given task as a scheduled task. | ||||||
|  |  | ||||||
|  |     - interval: The interval at which the task should be run | ||||||
|  |     - minutes: The number of minutes between task runs | ||||||
|  |  | ||||||
|  |     Example: | ||||||
|  |     ```python | ||||||
|  |     @register(ScheduledTask.DAILY) | ||||||
|  |     def my_custom_funciton(): | ||||||
|  |         ... | ||||||
|  |     ``` | ||||||
|  |     """ | ||||||
|  |  | ||||||
|  |     def _task_wrapper(admin_class): | ||||||
|  |         if not isinstance(admin_class, Callable): | ||||||
|  |             raise ValueError('Wrapped object must be a function') | ||||||
|  |  | ||||||
|  |         if interval not in ScheduledTask.TYPE: | ||||||
|  |             raise ValueError(f'Invalid interval. Must be one of {ScheduledTask.TYPE}') | ||||||
|  |  | ||||||
|  |         tasks.register(admin_class, interval, minutes=minutes) | ||||||
|  |  | ||||||
|  |         return admin_class | ||||||
|  |     return _task_wrapper | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @scheduled_task(ScheduledTask.MINUTES, 15) | ||||||
| def heartbeat(): | def heartbeat(): | ||||||
|     """Simple task which runs at 5 minute intervals, so we can determine that the background worker is actually running. |     """Simple task which runs at 5 minute intervals, so we can determine that the background worker is actually running. | ||||||
|  |  | ||||||
| @@ -149,6 +214,7 @@ def heartbeat(): | |||||||
|     heartbeats.delete() |     heartbeats.delete() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @scheduled_task(ScheduledTask.DAILY) | ||||||
| def delete_successful_tasks(): | def delete_successful_tasks(): | ||||||
|     """Delete successful task logs which are more than a month old.""" |     """Delete successful task logs which are more than a month old.""" | ||||||
|     try: |     try: | ||||||
| @@ -168,6 +234,7 @@ def delete_successful_tasks(): | |||||||
|         results.delete() |         results.delete() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @scheduled_task(ScheduledTask.DAILY) | ||||||
| def delete_old_error_logs(): | def delete_old_error_logs(): | ||||||
|     """Delete old error logs from the server.""" |     """Delete old error logs from the server.""" | ||||||
|     try: |     try: | ||||||
| @@ -190,6 +257,7 @@ def delete_old_error_logs(): | |||||||
|         return |         return | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @scheduled_task(ScheduledTask.DAILY) | ||||||
| def check_for_updates(): | def check_for_updates(): | ||||||
|     """Check if there is an update for InvenTree.""" |     """Check if there is an update for InvenTree.""" | ||||||
|     try: |     try: | ||||||
| @@ -232,6 +300,7 @@ def check_for_updates(): | |||||||
|     ) |     ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @scheduled_task(ScheduledTask.DAILY) | ||||||
| def update_exchange_rates(): | def update_exchange_rates(): | ||||||
|     """Update currency exchange rates.""" |     """Update currency exchange rates.""" | ||||||
|     try: |     try: | ||||||
|   | |||||||
| @@ -144,6 +144,7 @@ def notify_overdue_build_order(bo: build.models.Build): | |||||||
|     trigger_event(event_name, build_order=bo.pk) |     trigger_event(event_name, build_order=bo.pk) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @InvenTree.tasks.scheduled_task(InvenTree.tasks.ScheduledTask.DAILY) | ||||||
| def check_overdue_build_orders(): | def check_overdue_build_orders(): | ||||||
|     """Check if any outstanding BuildOrders have just become overdue |     """Check if any outstanding BuildOrders have just become overdue | ||||||
|  |  | ||||||
|   | |||||||
| @@ -5,9 +5,12 @@ from datetime import datetime, timedelta | |||||||
|  |  | ||||||
| from django.core.exceptions import AppRegistryNotReady | from django.core.exceptions import AppRegistryNotReady | ||||||
|  |  | ||||||
|  | from InvenTree.tasks import ScheduledTask, scheduled_task | ||||||
|  |  | ||||||
| logger = logging.getLogger('inventree') | logger = logging.getLogger('inventree') | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @scheduled_task(ScheduledTask.DAILY) | ||||||
| def delete_old_notifications(): | def delete_old_notifications(): | ||||||
|     """Remove old notifications from the database. |     """Remove old notifications from the database. | ||||||
|  |  | ||||||
|   | |||||||
| @@ -6,12 +6,13 @@ from django.utils.translation import gettext_lazy as _ | |||||||
|  |  | ||||||
| import common.notifications | import common.notifications | ||||||
| import InvenTree.helpers | import InvenTree.helpers | ||||||
| import InvenTree.tasks |  | ||||||
| import order.models | import order.models | ||||||
| from InvenTree.status_codes import PurchaseOrderStatus, SalesOrderStatus | from InvenTree.status_codes import PurchaseOrderStatus, SalesOrderStatus | ||||||
|  | from InvenTree.tasks import ScheduledTask, scheduled_task | ||||||
| from plugin.events import trigger_event | from plugin.events import trigger_event | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @scheduled_task(ScheduledTask.DAILY) | ||||||
| def notify_overdue_purchase_order(po: order.models.PurchaseOrder): | def notify_overdue_purchase_order(po: order.models.PurchaseOrder): | ||||||
|     """Notify users that a PurchaseOrder has just become 'overdue'""" |     """Notify users that a PurchaseOrder has just become 'overdue'""" | ||||||
|  |  | ||||||
| @@ -55,6 +56,7 @@ def notify_overdue_purchase_order(po: order.models.PurchaseOrder): | |||||||
|     ) |     ) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | @scheduled_task(ScheduledTask.DAILY) | ||||||
| def check_overdue_purchase_orders(): | def check_overdue_purchase_orders(): | ||||||
|     """Check if any outstanding PurchaseOrders have just become overdue: |     """Check if any outstanding PurchaseOrders have just become overdue: | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user