11import logging
22import sys
33import traceback
4- from typing import Any , List , Tuple
4+ import types
5+ import typing
56
67from django .contrib .contenttypes .fields import GenericForeignKey , GenericRelation
78from django .db import models , transaction
1516from django .views import View
1617from django .views .generic .edit import BaseCreateView
1718
18- from . import utils
1919from .conf import settings
20+ from .typing import HUMAN , MACHINE
2021from .utils import NoDashDiGraph
2122
2223logger = logging .getLogger (__name__ )
@@ -42,10 +43,10 @@ def __new__(mcs, name, bases, attrs):
4243 if func in nodes :
4344 node = getattr (klass , name )
4445 node .name = name
45- node .type = getattr (node , "type" , "machine" )
46+ node .type = getattr (node , "type" , MACHINE )
4647 node .workflow_cls = klass
4748 except TypeError :
48- pass
49+ pass # not a function
4950 if "override_view" in attrs and isinstance (klass .override_view , str ):
5051 klass .override_view = import_string (klass .override_view )
5152 if "detail_view" in attrs and isinstance (klass .detail_view , str ):
@@ -82,7 +83,7 @@ def save(self, **kwargs):
8283 update_fields .append ("modified" )
8384 super ().save (** kwargs )
8485
85- edges : List [ Tuple [ Any , Any ]] = None
86+ edges : list [ tuple [ typing . Any , typing . Any ]] = None
8687 """
8788 Edges define the transitions between tasks.
8889
@@ -136,9 +137,9 @@ def urls(cls):
136137 for name , node in cls .get_nodes ():
137138 if isinstance (node , View ):
138139 if isinstance (node , BaseCreateView ):
139- route = "{name}/" . format ( name = name )
140+ route = f "{ name } /"
140141 else :
141- route = "{name}/<int:pk>/" . format ( name = name )
142+ route = f "{ name } /<int:pk>/"
142143 urls .append (
143144 path (
144145 route + node .path ,
@@ -177,15 +178,11 @@ def get_url_namespace(cls):
177178
178179 def get_absolute_url (self ):
179180 """Return URL to workflow detail view."""
180- return reverse (
181- "{}:detail" .format (self .get_url_namespace ()), kwargs = dict (pk = self .pk )
182- )
181+ return reverse (f"{ self .get_url_namespace ()} :detail" , kwargs = dict (pk = self .pk ))
183182
184183 def get_override_url (self ):
185184 """Return URL to workflow override view."""
186- return reverse (
187- "{}:override" .format (self .get_url_namespace ()), kwargs = dict (pk = self .pk )
188- )
185+ return reverse (f"{ self .get_url_namespace ()} :override" , kwargs = dict (pk = self .pk ))
189186
190187 @classmethod
191188 def get_graph (cls , color = "black" ):
@@ -206,7 +203,7 @@ def get_graph(cls, color="black"):
206203 )
207204 for name , node in cls .get_nodes ():
208205 node_style = "filled"
209- if node .type == "human" :
206+ if node .type == HUMAN :
210207 node_style += ", rounded"
211208 graph .node (name , style = node_style , color = color , fontcolor = color )
212209
@@ -252,7 +249,7 @@ def get_instance_graph(self):
252249 style = "filled"
253250 peripheries = "1"
254251
255- if task .type == "human" :
252+ if task .type == HUMAN :
256253 style += ", rounded"
257254 if not task .completed :
258255 style += ", bold"
@@ -285,7 +282,7 @@ def get_instance_graph(self):
285282 for task in self .task_set .exclude (name__in = names ).exclude (name = "override" ):
286283 style = "filled, dashed"
287284 peripheries = "1"
288- if task .type == "human" :
285+ if task .type == HUMAN :
289286 style += ", rounded"
290287 if not task .completed :
291288 style += ", bold"
@@ -340,7 +337,7 @@ def workflow_state_subclasses():
340337
341338 apps .check_models_ready ()
342339 query = models .Q ()
343- for workflow in utils . get_workflows ():
340+ for workflow in get_workflows ():
344341 opts = workflow ._meta
345342 query |= models .Q (app_label = opts .app_label , model = opts .model_name )
346343 return query
@@ -396,8 +393,6 @@ class Task(models.Model):
396393
397394 name = models .CharField (max_length = 255 , db_index = True , editable = False )
398395
399- HUMAN = "human"
400- MACHINE = "machine"
401396 _type_choices = (
402397 (HUMAN , t (HUMAN )),
403398 (MACHINE , t (MACHINE )),
@@ -470,7 +465,7 @@ class Meta:
470465 default_manager_name = "objects"
471466
472467 def __str__ (self ):
473- return "%s (%s)" % ( self .name , self .pk )
468+ return f" { self .name } ( { self .pk } )"
474469
475470 def save (self , ** kwargs ):
476471 if self .pk :
@@ -486,12 +481,12 @@ def save(self, **kwargs):
486481
487482 def get_absolute_url (self ):
488483 if self .completed :
489- return
490- url_name = "{}:{}" . format ( self .workflow .get_url_namespace (), self .name )
484+ return # completed tasks have no detail view
485+ url_name = f" { self .workflow .get_url_namespace ()} : { self .name } "
491486 try :
492487 return reverse (url_name , kwargs = dict (pk = self .pk ))
493488 except NoReverseMatch :
494- pass
489+ return # no URL was defined for this task
495490
496491 @property
497492 def node (self ):
@@ -582,3 +577,23 @@ def start_next_tasks(self, next_nodes: list = None):
582577 transaction .on_commit (task .enqueue )
583578 tasks .append (task )
584579 return tasks
580+
581+
582+ def get_workflows () -> types .GeneratorType :
583+ """Return all registered workflows."""
584+ from django .apps import apps
585+
586+ apps .check_models_ready ()
587+ for model in apps .get_models ():
588+ if issubclass (model , Workflow ) and model is not Workflow and model .edges :
589+ yield model
590+ return # empty generator
591+
592+
593+ def get_workflow (name ) -> typing .Optional [Workflow ]:
594+ for workflow_cls in get_workflows ():
595+ if (
596+ name .lower ()
597+ == f"{ workflow_cls ._meta .app_label } .{ workflow_cls .__name__ } " .lower ()
598+ ):
599+ return workflow_cls
0 commit comments