Files
mayan-edms/apps/workflows/models.py
2012-05-23 00:14:56 -04:00

326 lines
11 KiB
Python

from __future__ import absolute_import
from django.db import models
from django.contrib.contenttypes import generic
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ugettext
from permissions.models import StoredPermission
from .literals import OPERAND_CHOICES, OPERAND_AND
#from .literals import NODE_TYPE_TASK, NODE_TYPE_START, NODE_TYPE_END
#NODE_TYPE_CHOICES
class Ability(models.Model):
label = models.CharField(max_length=128, unique=True, verbose_name = _(u'label'))
description = models.TextField(blank=True, verbose_name=_(u'description'))
def __unicode__(self):
return self.label
class Meta:
verbose_name = _(u'ability')
verbose_name_plural = _(u'abilities')
class Workflow(models.Model):
label = models.CharField(max_length=128, unique=True, verbose_name = _(u'label'))
initial_node = models.ForeignKey('WorkflowNode', related_name='workflow_initial_node', blank=True, null=True, verbose_name=_(u'initial node'))
initial_state = models.ForeignKey('WorkflowState', related_name='workflow_initial_state', blank=True, null=True, verbose_name=_(u'initial state'))
description = models.TextField(blank=True, verbose_name=_(u'description'))
def __unicode__(self):
return self.label
@property
def workflow_nodes(self):
return self.workflownode_set
def get_nodes(self):
return [workflow_node.node for workflow_node in self.workflownode_set.all()]
def add_node(self, node):
workflow_node = WorkflowNode(
workflow=self,
node=node)
workflow_node.save()
return workflow_node
def save(self, *args, **kwargs):
is_new = not self.pk
result = super(Workflow, self).save(*args, **kwargs)
if is_new:
# Instanciate a new start node
start_node = Start()
start_node.save()
# Set the start node a
workflow_node = self.add_node(node=start_node)
self.initial_node = workflow_node
self.save()
return result
class Meta:
verbose_name = _(u'workflow')
verbose_name_plural = _(u'workflows')
class Node(models.Model):
"""
Must provide:
possible_next_nodes()
Arguments: None
Returns:
List of possible nodes after this one executes
choices()
Arguments: workflow
Returns:
{
'next_node': workflow.nodes.all()
}
execute()
Arguments: workflow_instance
Returns: next_node
"""
label = models.CharField(max_length=128, verbose_name=_(u'label'))
description = models.TextField(blank=True, verbose_name=_(u'description'))
def __unicode__(self):
return self.label
class Meta:
verbose_name = _(u'state')
verbose_name_plural = _(u'states')
abstract = True
class Start(Node):
"""
The node with which all workflows start
"""
content_type = models.ForeignKey(ContentType, null=True)
object_id = models.PositiveIntegerField(null=True)
next_node = generic.GenericForeignKey(ct_field='content_type', fk_field='object_id')
def __unicode__(self):
return ugettext(u'Start')
def possible_next_nodes(self):
return self.next_node
def choices(self, workflow):
return {
'next_node': workflow.nodes.all()
}
def execute(self, workflow_instance):
return self.next_node
class Meta(Node.Meta):
verbose_name = _(u'start node')
verbose_name_plural = _(u'start nodes')
class End(Node):
class Meta(Node.Meta):
verbose_name = _(u'end node')
verbose_name_plural = _(u'end nodes')
'''
class Sequence(Node):
"""
A node that is enabled after the completion of a preceding node in the same workflow
"""
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
next_node = generic.GenericForeignKey(ct_field='content_type', fk_field='object_id')
#node_type = NODE_TYPE_TASK
def execute(self):
return self.next_node
class Meta(Node.Meta):
verbose_name = _(u'task')
verbose_name_plural = _(u'tasks')
'''
class State(models.Model):
label = models.CharField(max_length=128, verbose_name=_(u'label'))
description = models.TextField(blank=True, verbose_name=_(u'description'))
def __unicode__(self):
return self.label
class Meta:
verbose_name = _(u'state')
verbose_name_plural = _(u'states')
ordering = ('label',)
class WorkflowState(models.Model):
"""
List of possible states the object of this worflow could be in
"""
workflow = models.ForeignKey(Workflow, verbose_name=_(u'workflow'))
state = models.ForeignKey(State, verbose_name=_(u'state'))
description = models.TextField(blank=True, verbose_name=_(u'description'))
def __unicode__(self):
return unicode(self.state)
class Meta:
unique_together = ('workflow', 'state')
verbose_name = _(u'workflow state')
verbose_name_plural = _(u'workflows states')
# TODO: Reduntant - remove
class WorkflowNode(models.Model):
workflow = models.ForeignKey(Workflow, verbose_name=_(u'workflow'))
content_type = models.ForeignKey(ContentType)#, limit_choices_to={'model__in': ('model1', 'model2')})#, related_name='workflow_state_ability_object')#, blank=True, null=True)
object_id = models.PositiveIntegerField()#blank=True, null=True)
node = generic.GenericForeignKey(ct_field='content_type', fk_field='object_id')
description = models.TextField(blank=True, verbose_name=_(u'description'))
def __unicode__(self):
return unicode(self.node)
#def save(self, *args, **kwargs):
# if not issubclass(
# return super(WorkflowNode, self).save(*args, **kwargs)
class Meta:
#unique_together = ('workflow', 'state')
verbose_name = _(u'workflow node')
verbose_name_plural = _(u'workflows nodes')
"""
class WorkflowTaskAbilityGrant(models.Model):
workflow_state = models.ForeignKey(WorkflowState, related_name='workflow_state_ability', verbose_name=_(u'workflow state'))
content_type = models.ForeignKey(ContentType, related_name='workflow_state_ability_object')#, blank=True, null=True)
object_id = models.PositiveIntegerField()#blank=True, null=True)
content_object = generic.GenericForeignKey(ct_field='content_type', fk_field='object_id')
def __unicode__(self):
return unicode(self.content_object)
class Meta:
verbose_name = _(u'workflow state ability grant')
verbose_name_plural = _(u'workflows states ability grant')
#TODO: WorkflowStateACLEntry
#WorkflowState
#Actor
#Object
#Permission (s)
#TODO: WorkflowStateAlarm
#label
#timedate
#interval
class WorkflowStateTransition(models.Model):
workflow_state_source = models.ForeignKey(WorkflowState, verbose_name=_(u'workflow state source'))
transition = models.ForeignKey(Transition, related_name='workflow_state_transition', verbose_name=_(u'transition'))
workflow_state_destination = models.ForeignKey(WorkflowState, related_name='workflow_state_transition_destination', verbose_name=_(u'workflow state destination'))
description = models.TextField(blank=True, verbose_name=_(u'description'))
def __unicode__(self):
return unicode(self.transition)
class Meta:
verbose_name = _(u'workflow state transition')
verbose_name_plural = _(u'workflows states transitions')
class WorkflowStateTransitionAbility(models.Model):
attribute_comparison_operand = models.CharField(max_length=8, default=OPERAND_AND, choices=OPERAND_CHOICES, verbose_name=_(u'operand'))
negate = models.BooleanField(verbose_name=_(u'negate'), help_text=_(u'Inverts the attribute comparison.'))
ability = models.ForeignKey(Ability, related_name='workflow_state_transition_ability', verbose_name=_(u'ability'))
description = models.TextField(blank=True, verbose_name=_(u'description'))
def __unicode__(self):
return unicode(self.ability)
class Meta:
verbose_name = _(u'transition')
verbose_name_plural = _(u'transitions')
"""
class WorkflowInstance(models.Model):
workflow = models.ForeignKey(Workflow, verbose_name=_(u'workflow'))
content_type = models.ForeignKey(ContentType, verbose_name=_(u'Content type'), related_name='workflow_instance_object')#, blank=True, null=True)
object_id = models.PositiveIntegerField()#blank=True, null=True)
content_object = generic.GenericForeignKey(ct_field='content_type', fk_field='object_id')
def __unicode__(self):
return unicode(self.content_object)
@property
def active_nodes(self):
return self.workflowinstanceactivenode_set
def set_active_state(self, state):
active_state = self.get_active_state()
if active_state:
active_state.delete()
# Trigger an exception if the state argument if not allowed for this workflow
state = WorkflowState.objects.get(workflow=self.workflow, state=state).state
self.workflowinstanceactivestate_set.create(
workflow_instance = self,
state = state
)
def get_active_state(self):
try:
return self.workflowinstanceactivestate_set.get().state
except WorkflowInstanceActiveState.DoesNotExist:
return None
active_state = property(get_active_state, set_active_state)
class Meta:
verbose_name = _(u'workflow instance')
verbose_name_plural = _(u'workflow instances')
# unique_together = ('content_type', 'object_id', 'workflow')
class WorkflowInstanceActiveNode(models.Model):
workflow_instance = models.ForeignKey(WorkflowInstance, verbose_name=_(u'workflow instance'))
workflow_node = models.ForeignKey(WorkflowNode, verbose_name=_(u'workflow node'))
class Meta:
verbose_name = _(u'workflow instance active node')
verbose_name_plural = _(u'workflow instances active nodes')
class WorkflowInstanceState(models.Model):
"""
This class holds the active state for the workflow instance
"""
workflow_instance = models.ForeignKey(WorkflowInstance, verbose_name=_(u'workflow instance'))
state = models.ForeignKey(State, null=True, verbose_name=_(u'state'))
class Meta:
unique_together = ('workflow_instance', 'state')
verbose_name = _(u'workflow instance active state')
verbose_name_plural = _(u'workflow instances active states')
# TODO: WorkflowInstanceActiveNodeHistory
# TODO: WorkflowInstanceActiveStateHistory