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:
Matthias Mair 2022-10-17 00:53:59 +02:00 committed by GitHub
parent 182bc29053
commit 0bea2c7b56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 84 additions and 61 deletions

View File

@ -54,68 +54,16 @@ class InvenTreeConfig(AppConfig):
def start_background_tasks(self):
"""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...")
# Remove successful task results from the database
InvenTree.tasks.schedule_task(
'InvenTree.tasks.delete_successful_tasks',
schedule_type=Schedule.DAILY,
)
# 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
)
# Run through registered tasks
for task in InvenTree.tasks.tasks.task_list:
InvenTree.tasks.schedule_task(
task.func,
schedule_type=task.interval,
minutes=task.minutes,
)
logger.info("Started background tasks...")
# Make regular backups
InvenTree.tasks.schedule_task(

View File

@ -4,7 +4,9 @@ import json
import logging
import re
import warnings
from dataclasses import dataclass
from datetime import timedelta
from typing import Callable
from django.conf import settings
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)
@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():
"""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()
@scheduled_task(ScheduledTask.DAILY)
def delete_successful_tasks():
"""Delete successful task logs which are more than a month old."""
try:
@ -168,6 +234,7 @@ def delete_successful_tasks():
results.delete()
@scheduled_task(ScheduledTask.DAILY)
def delete_old_error_logs():
"""Delete old error logs from the server."""
try:
@ -190,6 +257,7 @@ def delete_old_error_logs():
return
@scheduled_task(ScheduledTask.DAILY)
def check_for_updates():
"""Check if there is an update for InvenTree."""
try:
@ -232,6 +300,7 @@ def check_for_updates():
)
@scheduled_task(ScheduledTask.DAILY)
def update_exchange_rates():
"""Update currency exchange rates."""
try:

View File

@ -144,6 +144,7 @@ def notify_overdue_build_order(bo: build.models.Build):
trigger_event(event_name, build_order=bo.pk)
@InvenTree.tasks.scheduled_task(InvenTree.tasks.ScheduledTask.DAILY)
def check_overdue_build_orders():
"""Check if any outstanding BuildOrders have just become overdue

View File

@ -5,9 +5,12 @@ from datetime import datetime, timedelta
from django.core.exceptions import AppRegistryNotReady
from InvenTree.tasks import ScheduledTask, scheduled_task
logger = logging.getLogger('inventree')
@scheduled_task(ScheduledTask.DAILY)
def delete_old_notifications():
"""Remove old notifications from the database.

View File

@ -6,12 +6,13 @@ from django.utils.translation import gettext_lazy as _
import common.notifications
import InvenTree.helpers
import InvenTree.tasks
import order.models
from InvenTree.status_codes import PurchaseOrderStatus, SalesOrderStatus
from InvenTree.tasks import ScheduledTask, scheduled_task
from plugin.events import trigger_event
@scheduled_task(ScheduledTask.DAILY)
def notify_overdue_purchase_order(po: order.models.PurchaseOrder):
"""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():
"""Check if any outstanding PurchaseOrders have just become overdue: