From 9b542ed23f84e472c994748117e69803c91d4721 Mon Sep 17 00:00:00 2001 From: eeintech Date: Thu, 22 Jul 2021 15:55:17 -0400 Subject: [PATCH 01/20] Run exchange rate updated as task if worker cluster is running --- InvenTree/InvenTree/views.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py index 4b559642ca..80d0a015ff 100644 --- a/InvenTree/InvenTree/views.py +++ b/InvenTree/InvenTree/views.py @@ -27,6 +27,7 @@ from stock.models import StockLocation, StockItem from common.models import InvenTreeSetting, ColorTheme from users.models import check_user_role, RuleSet +import InvenTree.status import InvenTree.tasks from .forms import DeleteForm, EditUserForm, SetPasswordForm @@ -802,8 +803,17 @@ class CurrencyRefreshView(RedirectView): On a POST request we will attempt to refresh the exchange rates """ - # Will block for a little bit - InvenTree.tasks.update_exchange_rates() + # Define associated task + task_name = 'InvenTree.tasks.update_exchange_rates' + + if InvenTree.status.is_worker_running(): + # Running as task + InvenTree.tasks.offload_task(task_name) + else: + # Retrieve function from task name + _func = eval(task_name) + # Run it: will block for a little bit + _func() return self.get(request, *args, **kwargs) From d7028b6d744991148bcacb6e2eb260c1733ffed0 Mon Sep 17 00:00:00 2001 From: eeintech Date: Thu, 22 Jul 2021 16:34:35 -0400 Subject: [PATCH 02/20] Make it generic method instead --- InvenTree/InvenTree/tasks.py | 46 ++++++++++++++++++++++++++++++++++++ InvenTree/InvenTree/views.py | 14 ++++------- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/InvenTree/InvenTree/tasks.py b/InvenTree/InvenTree/tasks.py index cab87ee64b..1c77febf79 100644 --- a/InvenTree/InvenTree/tasks.py +++ b/InvenTree/InvenTree/tasks.py @@ -69,6 +69,47 @@ def offload_task(taskname, *args, **kwargs): task.run() +def run_task(taskname): + """ + 1. Check if task is implemented + - yes: proceed + - no: return + 2. Check if worker cluster is running + - yes: add task to queue + - no: run it as blocking process + """ + + # Get task list + tasks = get_task_list() + + # Check if task exists + if taskname not in tasks: + logger.warning(f'Task "{taskname}" is not implemented') + return + + from InvenTree.status import is_worker_running + + if is_worker_running(): + # Append module path + taskname = 'InvenTree.tasks.' + taskname + # Running as task + offload_task(taskname) + else: + # Retrieve local method from task name + _func = eval(taskname) + # Run it as blocking process + _func() + + +def get_task_list(): + return [task for task in LOCAL_METHODS if task not in TASK_MANAGEMENT] + + +# Keep TASK_MANAGEMENT before task methods +TASK_MANAGEMENT = [key for key, value in locals().items() if callable(value) and value.__module__ == __name__] +# + + def heartbeat(): """ Simple task which runs at 5 minute intervals, @@ -217,3 +258,8 @@ def send_email(subject, body, recipients, from_email=None): from_email, recipients, ) + + +# Keep LOCAL_METHODS at the end of the file +LOCAL_METHODS = [key for key, value in locals().items() if callable(value) and value.__module__ == __name__] +# diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py index 80d0a015ff..88199f39a0 100644 --- a/InvenTree/InvenTree/views.py +++ b/InvenTree/InvenTree/views.py @@ -803,17 +803,11 @@ class CurrencyRefreshView(RedirectView): On a POST request we will attempt to refresh the exchange rates """ - # Define associated task - task_name = 'InvenTree.tasks.update_exchange_rates' + # Define associated task from InvenTree.tasks list of methods + taskname = 'update_exchange_rates' - if InvenTree.status.is_worker_running(): - # Running as task - InvenTree.tasks.offload_task(task_name) - else: - # Retrieve function from task name - _func = eval(task_name) - # Run it: will block for a little bit - _func() + # Run it + InvenTree.tasks.run_task(taskname) return self.get(request, *args, **kwargs) From 3f44233074525ceb89f215e3c0975fe129c6bd9b Mon Sep 17 00:00:00 2001 From: eeintech Date: Thu, 22 Jul 2021 16:41:45 -0400 Subject: [PATCH 03/20] Improve import --- InvenTree/InvenTree/views.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py index 88199f39a0..a783e8fcb3 100644 --- a/InvenTree/InvenTree/views.py +++ b/InvenTree/InvenTree/views.py @@ -27,9 +27,6 @@ from stock.models import StockLocation, StockItem from common.models import InvenTreeSetting, ColorTheme from users.models import check_user_role, RuleSet -import InvenTree.status -import InvenTree.tasks - from .forms import DeleteForm, EditUserForm, SetPasswordForm from .forms import ColorThemeSelectForm, SettingCategorySelectForm from .helpers import str2bool @@ -803,11 +800,13 @@ class CurrencyRefreshView(RedirectView): On a POST request we will attempt to refresh the exchange rates """ + from InvenTree.tasks import run_task + # Define associated task from InvenTree.tasks list of methods taskname = 'update_exchange_rates' # Run it - InvenTree.tasks.run_task(taskname) + run_task(taskname) return self.get(request, *args, **kwargs) From fbdf11e6e7d68062c90083dbd368f6bb2fc4e279 Mon Sep 17 00:00:00 2001 From: eeintech Date: Wed, 4 Aug 2021 11:23:52 -0400 Subject: [PATCH 04/20] Merged run_task code into offload_task function Added option to force synchronous operation Use that option for update_exchange_rates --- InvenTree/InvenTree/tasks.py | 47 +++++++++++++++--------------------- InvenTree/InvenTree/views.py | 4 +-- 2 files changed, 21 insertions(+), 30 deletions(-) diff --git a/InvenTree/InvenTree/tasks.py b/InvenTree/InvenTree/tasks.py index 1c77febf79..efe1361bd1 100644 --- a/InvenTree/InvenTree/tasks.py +++ b/InvenTree/InvenTree/tasks.py @@ -51,32 +51,17 @@ def schedule_task(taskname, **kwargs): pass -def offload_task(taskname, *args, **kwargs): - """ - Create an AsyncTask. - This is different to a 'scheduled' task, - in that it only runs once! +def offload_task(taskname, force_sync=False, *args, **kwargs): """ + First check if the task method pointed + by taskname is implemented inside this file. - try: - from django_q.tasks import AsyncTask - except (AppRegistryNotReady): - logger.warning("Could not offload task - app registry not ready") - return + Then create an AsyncTask if workers are running. + This is different to a 'scheduled' task, + in that it only runs once! - task = AsyncTask(taskname, *args, **kwargs) - - task.run() - - -def run_task(taskname): - """ - 1. Check if task is implemented - - yes: proceed - - no: return - 2. Check if worker cluster is running - - yes: add task to queue - - no: run it as blocking process + If workers are not running or force_sync flag + is set then the task is ran synchronously. """ # Get task list @@ -84,20 +69,26 @@ def run_task(taskname): # Check if task exists if taskname not in tasks: - logger.warning(f'Task "{taskname}" is not implemented') + logger.warning(f'Task "{taskname}" is not implemented in InvenTree/tasks.py') return + try: + from django_q.tasks import AsyncTask + except (AppRegistryNotReady): + logger.warning("Could not offload task - app registry not ready") + return from InvenTree.status import is_worker_running - if is_worker_running(): + if is_worker_running() and not force_sync: # Append module path taskname = 'InvenTree.tasks.' + taskname - # Running as task - offload_task(taskname) + # Running as asynchronous task + task = AsyncTask(taskname, *args, **kwargs) + task.run() else: # Retrieve local method from task name _func = eval(taskname) - # Run it as blocking process + # Run it as synchronous task _func() diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py index b27b58b40d..03fee01837 100644 --- a/InvenTree/InvenTree/views.py +++ b/InvenTree/InvenTree/views.py @@ -815,13 +815,13 @@ class CurrencyRefreshView(RedirectView): On a POST request we will attempt to refresh the exchange rates """ - from InvenTree.tasks import run_task + from InvenTree.tasks import offload_task # Define associated task from InvenTree.tasks list of methods taskname = 'update_exchange_rates' # Run it - run_task(taskname) + offload_task(taskname, force_sync=True) return redirect(reverse_lazy('settings')) From 1eb8a9f310aa99a87bb20fd27c58e8ebfd0c585c Mon Sep 17 00:00:00 2001 From: eeintech Date: Thu, 5 Aug 2021 17:39:08 -0400 Subject: [PATCH 05/20] BOM upload templates fixes --- .../part/bom_upload/match_fields.html | 2 +- .../part/bom_upload/upload_file.html | 93 ++++++++++++------- 2 files changed, 61 insertions(+), 34 deletions(-) diff --git a/InvenTree/part/templates/part/bom_upload/match_fields.html b/InvenTree/part/templates/part/bom_upload/match_fields.html index d1f325aaee..3bd51c1855 100644 --- a/InvenTree/part/templates/part/bom_upload/match_fields.html +++ b/InvenTree/part/templates/part/bom_upload/match_fields.html @@ -5,7 +5,7 @@ {% block form_alert %} {% if missing_columns and missing_columns|length > 0 %} -