1
1
import logging
2
2
import sys
3
3
import traceback
4
- from typing import Any , List , Tuple
4
+ import types
5
+ import typing
5
6
6
7
from django .contrib .contenttypes .fields import GenericForeignKey , GenericRelation
7
8
from django .db import models , transaction
15
16
from django .views import View
16
17
from django .views .generic .edit import BaseCreateView
17
18
18
- from . import utils
19
19
from .conf import settings
20
+ from .typing import HUMAN , MACHINE
20
21
from .utils import NoDashDiGraph
21
22
22
23
logger = logging .getLogger (__name__ )
@@ -42,10 +43,10 @@ def __new__(mcs, name, bases, attrs):
42
43
if func in nodes :
43
44
node = getattr (klass , name )
44
45
node .name = name
45
- node .type = getattr (node , "type" , "machine" )
46
+ node .type = getattr (node , "type" , MACHINE )
46
47
node .workflow_cls = klass
47
48
except TypeError :
48
- pass
49
+ pass # not a function
49
50
if "override_view" in attrs and isinstance (klass .override_view , str ):
50
51
klass .override_view = import_string (klass .override_view )
51
52
if "detail_view" in attrs and isinstance (klass .detail_view , str ):
@@ -82,7 +83,7 @@ def save(self, **kwargs):
82
83
update_fields .append ("modified" )
83
84
super ().save (** kwargs )
84
85
85
- edges : List [ Tuple [ Any , Any ]] = None
86
+ edges : list [ tuple [ typing . Any , typing . Any ]] = None
86
87
"""
87
88
Edges define the transitions between tasks.
88
89
@@ -136,9 +137,9 @@ def urls(cls):
136
137
for name , node in cls .get_nodes ():
137
138
if isinstance (node , View ):
138
139
if isinstance (node , BaseCreateView ):
139
- route = "{name}/" . format ( name = name )
140
+ route = f "{ name } /"
140
141
else :
141
- route = "{name}/<int:pk>/" . format ( name = name )
142
+ route = f "{ name } /<int:pk>/"
142
143
urls .append (
143
144
path (
144
145
route + node .path ,
@@ -177,15 +178,11 @@ def get_url_namespace(cls):
177
178
178
179
def get_absolute_url (self ):
179
180
"""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 ))
183
182
184
183
def get_override_url (self ):
185
184
"""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 ))
189
186
190
187
@classmethod
191
188
def get_graph (cls , color = "black" ):
@@ -206,7 +203,7 @@ def get_graph(cls, color="black"):
206
203
)
207
204
for name , node in cls .get_nodes ():
208
205
node_style = "filled"
209
- if node .type == "human" :
206
+ if node .type == HUMAN :
210
207
node_style += ", rounded"
211
208
graph .node (name , style = node_style , color = color , fontcolor = color )
212
209
@@ -252,7 +249,7 @@ def get_instance_graph(self):
252
249
style = "filled"
253
250
peripheries = "1"
254
251
255
- if task .type == "human" :
252
+ if task .type == HUMAN :
256
253
style += ", rounded"
257
254
if not task .completed :
258
255
style += ", bold"
@@ -285,7 +282,7 @@ def get_instance_graph(self):
285
282
for task in self .task_set .exclude (name__in = names ).exclude (name = "override" ):
286
283
style = "filled, dashed"
287
284
peripheries = "1"
288
- if task .type == "human" :
285
+ if task .type == HUMAN :
289
286
style += ", rounded"
290
287
if not task .completed :
291
288
style += ", bold"
@@ -340,7 +337,7 @@ def workflow_state_subclasses():
340
337
341
338
apps .check_models_ready ()
342
339
query = models .Q ()
343
- for workflow in utils . get_workflows ():
340
+ for workflow in get_workflows ():
344
341
opts = workflow ._meta
345
342
query |= models .Q (app_label = opts .app_label , model = opts .model_name )
346
343
return query
@@ -396,8 +393,6 @@ class Task(models.Model):
396
393
397
394
name = models .CharField (max_length = 255 , db_index = True , editable = False )
398
395
399
- HUMAN = "human"
400
- MACHINE = "machine"
401
396
_type_choices = (
402
397
(HUMAN , t (HUMAN )),
403
398
(MACHINE , t (MACHINE )),
@@ -470,7 +465,7 @@ class Meta:
470
465
default_manager_name = "objects"
471
466
472
467
def __str__ (self ):
473
- return "%s (%s)" % ( self .name , self .pk )
468
+ return f" { self .name } ( { self .pk } )"
474
469
475
470
def save (self , ** kwargs ):
476
471
if self .pk :
@@ -486,12 +481,12 @@ def save(self, **kwargs):
486
481
487
482
def get_absolute_url (self ):
488
483
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 } "
491
486
try :
492
487
return reverse (url_name , kwargs = dict (pk = self .pk ))
493
488
except NoReverseMatch :
494
- pass
489
+ return # no URL was defined for this task
495
490
496
491
@property
497
492
def node (self ):
@@ -582,3 +577,23 @@ def start_next_tasks(self, next_nodes: list = None):
582
577
transaction .on_commit (task .enqueue )
583
578
tasks .append (task )
584
579
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