From f74bbda314f3e2434d10cebfbb50f7991ba51a49 Mon Sep 17 00:00:00 2001 From: Mikhail Podgurskiy Date: Mon, 10 Jul 2023 16:33:40 +0600 Subject: [PATCH] Changes sync --- CHANGELOG.rst | 7 +++-- README.md | 14 ++++------ setup.py | 2 +- viewflow/workflow/flow/views/dashboard.py | 6 ++++- viewflow/workflow/managers.py | 33 ++++++++++++++--------- viewflow/workflow/utils.py | 32 ++++++++++++++++++++-- 6 files changed, 66 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5575d80..38b7f4b 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,10 +2,13 @@ Changelog ========= -GIT VERSION ------------ +2.0.0.b5 2023-07-10 +------------------- - Alow attach layout to forms in default form rendering template +- Fix subprocess node activation +- Added db indexes for workflow models +- Improve workflow REST API support 2.0.0.b4 2023-06-05 ------------------- diff --git a/README.md b/README.md index 7068c54..ebf86da 100644 --- a/README.md +++ b/README.md @@ -179,17 +179,13 @@ modifications of Viewflow. You can find the commercial license terms in ## Changelog -2.0.0.b4 2023-06-05 +2.0.0.b5 2023-07-10 ------------------- -- New flow.SplitFirst Node -- New celery.Timer Node -- Expose REST API with drf-spectacular -- Expose list_ordering_fields in a ModelViewset -- Retain history and return to the Inbox/Queue list views after completing a flow task -- Enable smooth page transitions in Chrome/Safari -- Hotwire/Turbo integration for Django Admin with viewflow.contrib.admin app -- Resolved issue with viewflow.fsm reporting unmet condition messages +- Alow attach layout to forms in default form rendering template +- Fix subprocess node activation +- Added db indexes for workflow models +- Improve workflow REST API support diff --git a/setup.py b/setup.py index 1962e76..12d974c 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ README = open("README.md", "r", encoding="utf-8").read() setuptools.setup( name="django-viewflow", - version="2.0.0b4", + version="2.0.0b5", author_email="kmmbvnr@gmail.com", author="Mikhail Podgurskiy", description="Reusable library to build business applications fast", diff --git a/viewflow/workflow/flow/views/dashboard.py b/viewflow/workflow/flow/views/dashboard.py index 178231a..f1ef746 100644 --- a/viewflow/workflow/flow/views/dashboard.py +++ b/viewflow/workflow/flow/views/dashboard.py @@ -34,7 +34,11 @@ class DashboardView( def get_context_data(self, **kwargs): sorted_nodes, _ = chart.topsort(self.flow_class) - nodes = [node for node in sorted_nodes if node.task_type in ["HUMAN", "JOB"]] + nodes = [ + node + for node in sorted_nodes + if node.task_type in ["HUMAN", "JOB", "SUBPROCESS"] + ] start_nodes = [ {"node": node, "can_execute": node.can_execute(self.request.user)} diff --git a/viewflow/workflow/managers.py b/viewflow/workflow/managers.py index 8e62612..599caba 100644 --- a/viewflow/workflow/managers.py +++ b/viewflow/workflow/managers.py @@ -8,6 +8,7 @@ from django.db.models.constants import LOOKUP_SEP from django.db.models.query import ModelIterable from .status import STATUS +from .utils import get_next_process_task def _available_flows(flow_classes, user): @@ -257,22 +258,28 @@ class TaskQuerySet(QuerySet): Prefer assigned tasks first, if not, return first task from user queue """ - # first try to find an assigned task - task = self.filter(process=process, owner=user, status=STATUS.ASSIGNED).first() + # task inside a same process + task = get_next_process_task(self, process, user) - # lookup for a task in a queue + # task inside subprocess if task is None: - task = ( - self.user_queue(user).filter(process=process, status=STATUS.NEW).first() - ) + subprocess_task = self.filter(process__parent_task__process=process).first() + if subprocess_task: + task = get_next_process_task(self, subprocess_task.process, user) - # lookup for a job - if task is None: - task = ( - self.filter(process=process, flow_task_type='JOB', status__in=[ - STATUS.NEW, STATUS.SCHEDULED, STATUS.STARTED - ]).first() - ) + # task inside parent process + if task is None and process.parent_task_id: + task = get_next_process_task(self, process.parent_task.process, user) + + # task inside other subprocesses of parent task + if task is None and process.parent_task_id: + processes = process.__class__._default_manager.filter( + parent_task__process=process.parent_task.process + ).exclude(pk=process.pk) + for sub_process in processes: + task = get_next_process_task(self, sub_process, user) + if task: + break return task diff --git a/viewflow/workflow/utils.py b/viewflow/workflow/utils.py index f7b935d..b270b8b 100644 --- a/viewflow/workflow/utils.py +++ b/viewflow/workflow/utils.py @@ -1,20 +1,48 @@ +from .status import STATUS + + class Act(object): """Shortcut to access activation data.""" + @property def process(self): - """ Shortcut for lambda activation: activation.process...)""" + """Shortcut for lambda activation: activation.process...)""" + class Lookup(object): def __getattribute__(self, name): return lambda activation: getattr(activation.process, name) + return Lookup() @property def task(self): - """ Shortcut for lambda activation: activation.task...)""" + """Shortcut for lambda activation: activation.task...)""" + class Lookup(object): def __getattribute__(self, name): return lambda activation: getattr(activation.task, name) + return Lookup() act = Act() + + +def get_next_process_task(manager, process, user): + task = manager.filter(process=process, owner=user, status=STATUS.ASSIGNED).first() + + # lookup for a task in a queue + if task is None: + task = ( + manager.user_queue(user).filter(process=process, status=STATUS.NEW).first() + ) + + # lookup for a job + if task is None: + task = manager.filter( + process=process, + flow_task_type="JOB", + status__in=[STATUS.NEW, STATUS.SCHEDULED, STATUS.STARTED], + ).first() + + return task