Permalink
Browse files

first commit - models.py and a long README.txt

  • Loading branch information...
0 parents commit 68f5f3a11cd0f04cf954a776ae05591576703af2 @ntoll committed May 14, 2009
Showing with 326 additions and 0 deletions.
  1. +32 −0 LICENSE.txt
  2. +92 −0 README.txt
  3. 0 __init__.py
  4. +201 −0 models.py
  5. +1 −0 views.py
32 LICENSE.txt
@@ -0,0 +1,32 @@
+Copyright (c) 2009 Nicholas H.Tollervey (http://ntoll.org/contact)
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in
+the documentation and/or other materials provided with the
+distribution.
+* Neither the name of ntoll.org nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior written
+permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
92 README.txt
@@ -0,0 +1,92 @@
+Django Workflow Application v0.1 (alpha)
+
+(c) 2009 Nicholas H.Tollervey (http://ntoll.org/contact)
+
+See the file LICENSE.txt for the licensing terms and conditions.
+
+This Django application provides a workflow engine for use in your
+web-application. This work is an abstraction of / extraction from a workflow
+engine built for an HR application.
+
+Currently only the models have been "extracted".
+
+To make things easy I've described each model below and made up a couple of
+"user stories" to illustrate how stuff fits together (an Applicant Tracking
+System used by HR and a simple issue tracker for filing bugs, feature requests
+and whatnot).
+
+I realise the examples might be a tad contrived but I always find that thinking
+about tangible examples relating to data models is always helpful.
+
+The order that I describe the tables follows the code outline.
+
+Role - defines what sort of users are associated with different aspects of the
+workflow. In the HR project these roles might include: 'HR Consultant', 'Hiring
+Manager', 'Interviewer' or 'Assessor'. In the issue tracker these might simply
+be 'Core Developer', 'Release Manager' or 'Tester'. The State and Transition
+models have a many-to-many relationship with Role to indicate what sort of
+person has permission to view a state or use a transition. The Event model has a
+many-to-many relationship with Role to indicate who is participating in the
+event.
+
+Workflow - names / describes a workflow. In the HR project workflows might be:
+'Generic Hiring Process', 'Executive Hiring Process' or 'Employee Appraisal'. In
+the issue tracker these might include: 'Bug Report', 'Feature Request' or
+'Release Lifecycle'. A workflow can be in one of three states that defines the
+life of a workflow: definition -> active -> retired. A workflow can only be
+changed when it has the state 'definition' (where changed means states and
+transitions added or removed). A workflow can only be used when it has the state
+'active'. When a workflow is no longer useful, or is found to contain errors
+then it is marked as 'retired'. Why take so much trouble over this? Imagine the
+mess that could ensue if a 'live' workflow were changed and states were deleted
+or orphaned. Furthermore, retired workflows could be "cloned" as the basis of
+new workflows in the 'definition' state.
+
+State - represents a specific state that a thing can be in. In the HR project
+examples might be: 'Open for applications', 'Final shortlisting', 'Employee
+Interviews'. In the issue tracker we might have: 'Open', 'Rejected', 'Awaiting
+Approval'. Put simply a state is a description of a node in a directed graph.
+Only one state can be marked as 'is_start_state' for each workflow but many can
+be marked as 'is_end_state' (indicating the workflow has been completed). Roles
+associated with each state indicate *who* has access to the thing when in this
+state.
+
+Transition - defines how a workflow can move between states. They *should* be
+given "active" names. For example, in the HR project transitions might be:
+'Publish Vacancy' (leading to the 'Open for applications' state), 'Publish
+applications' (leading to the 'Final shortlisting' state) or 'Publish
+meeting slots' (leading to 'Employee Interviews'). The issue tracker transitions
+are probably more obvious 'Submit issue' (leads to 'Open' state), 'Reject'
+(leads to 'Rejected') and 'Propose new feature' (leads to 'Awaiting Approval').
+Put simply, a transition is an edge in a directed graph. Roles associated with
+each transition indicate *who* can use the transition to move the workflow to a
+new state.
+
+Event - is a specification for something that is supposed to happen whilst in
+this state. In the HR project events might be: 'Meeting to approve job
+specification', 'Meeting of review board' or 'Contact all managers conducting
+employee interviews'. The issue tracker might have: 'Check for duplicate issue',
+'Check unit tests pass on staging server'. The roles field indicates *who* is to
+participate in the event. In my original application I included fields for cost
+estimation and an is_mandatory flag.
+
+WorkflowManager - is a core model to link "things" to workflows in a similar way
+to User objects having a profile. Vacancy and Issue instances in the HR and
+issue tracker examples should reference a WorkflowManager. The WorkflowManager
+simply references an active Workflow and contains created_on and completed_on
+timestamps.
+
+Participant - links django.contrib.auth.models.User instances to a Role and
+WorkflowManager. This way we know that user 'Joe Blogs' has the role 'Hiring
+Manager' for the duration of the WorkflowManager that is using the 'Executive
+Hiring Process' workflow. We might also know that jtauber is Release Manager
+during the lifetime of the WorkflowManager for Pinax1.0 that follows the Release
+Lifecycle workflow.
+
+WorkflowEvent - simply links up the WorkflowManager, State, Transition that got
+to the State, Participant and a timestamp enabling us to know how the workflow
+has progressed (with the most recent being the current state). Examples might
+be: Pinax1.0 WorkflowManager, "RC1" state, "Prepare Release Candidate"
+transition, jtauber, 2009-11-5.
+
+As always, comments, ideas, suggestions and improvements are most welcome.
0 __init__.py
No changes.
201 models.py
@@ -0,0 +1,201 @@
+# -*- coding: UTF-8 -*-
+"""
+Models for Workflows.
+
+Copyright (c) 2009 Nicholas H.Tollervey (http://ntoll.org/contact)
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimer in
+the documentation and/or other materials provided with the
+distribution.
+* Neither the name of ntoll.org nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior written
+permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+"""
+from django.db import models
+from django.utils.translation import ugettext_lazy as _, ugettext as __
+from django.contrib.auth.models import User
+
+class Role(models.Model):
+ """
+ Represents a type of user who can be associated with a workflow. Used by
+ the State and Transition models to define *who* has permission to view a
+ state or use a transition.
+ """
+ name = models.CharField(
+ _('Name of Role'),
+ max_length=64
+ )
+ description = models.TextField(
+ _('Description'),
+ blank=True
+ )
+
+class Workflow(models.Model):
+ """
+ Instances of this class represent a named workflow that achieve a particular
+ aim through a series of related states / transitions. A name for a directed
+ graph.
+ """
+
+ # A workflow can be in one of three states:
+ #
+ # * definition: you're building the thing to meet whatever requirements you
+ # have
+ #
+ # * active: you're using the defined workflow in relation to things in your
+ # application - the workflow definition is frozen from this point on.
+ #
+ # * retired: you no longer use the workflow (but we keep it so it can be
+ # cloned as the basis of new workflows starting in the definition state)
+ #
+ # Why do this? Imagine the mess that could be created if a "live" workflow
+ # was edited and states were deleted or orphaned. These states at least
+ # allow us to check things don't go horribly wrong. :-/
+ DEFINITION = 0
+ ACTIVE = 1
+ RETIRED = 2
+
+ STATUS_CHOICE_LIST = (
+ (DEFINITION, _('In definition')),
+ (ACTIVE, _('Active')),
+ (RETIRED, _('Retired')),
+ )
+
+ name = models.SlugField(
+ _('Workflow Name')
+ )
+ description = models.TextField(
+ _('Description'),
+ blank=True
+ )
+ status = models.IntegerField(
+ _('Status'),
+ choices=STATUS_CHOICE_LIST,
+ default = DEFINITION
+ )
+
+class State(models.Model):
+ """
+ Represents a specific state that a thing can be in during its progress
+ through a workflow. A node in a directed graph.
+ """
+ name = models.CharField(
+ _('Name'),
+ max_length=256
+ )
+ description = models.TextField(
+ _('Description'),
+ blank=True
+ )
+ is_start_state = models.BooleanField(
+ _('Is the start state?'),
+ help_text=_('There can only be one start state for a workflow'),
+ default=False
+ )
+ is_end_state = models.BooleanField(
+ _('Is an end state?'),
+ help_text=_('An end state shows that the workflow is complete'),
+ default=False
+ )
+ workflow = models.ForeignKey(Workflow)
+ # The roles defined here define *who* has permission to view the item in
+ # this state.
+ roles = models.ManyToManyField(Role)
+ # My original workflow State model included fields to allow for estimation
+ # of duration of this state. Managers seemed to like this feature!
+
+class Transition(models.Model):
+ """
+ Represents how a workflow can move between different states. An edge
+ between state "nodes" in a directed graph.
+ """
+ name = models.CharField(
+ _('Name of transition'),
+ max_length=128,
+ help_text=_('Use an "active" verb. e.g. "Close Issue"')
+ )
+ from_state = models.ForeignKey(
+ State,
+ related_name = 'next_actions'
+ )
+ to_state = models.ForeignKey(
+ State,
+ related_name = 'actions_into'
+ )
+ # The roles referenced here define *who* has permission to use this
+ # transition to move between states.
+ roles = models.ManyToManyField(Role)
+
+class Event(models.Model):
+ """
+ A definition of something that is supposed to happen when in a particular
+ state.
+ """
+ name = models.CharField(
+ _('Event summary'),
+ max_length=256
+ )
+ description = models.TextField(
+ _('Description'),
+ blank=True
+ )
+ # The roles referenced here indicate *who* is supposed to be a part of the
+ # event
+ roles = models.ManyToManyField(Role)
+ # In my original workflow the Event model included a "cost" and is_mandatory
+ # field. Again, much loved by managers...
+
+class WorkflowManager(models.Model):
+ """
+ Other models in the project reference this model so they are associated with
+ a particular workflow.
+ """
+ workflow = models.ForeignKey(Workflow)
+ created_on = models.DateTimeField(auto_now_add=True)
+ completed_on = models.DateTimeField(
+ null=True,
+ blank=True
+ )
+
+class Participant(models.Model):
+ """
+ Defines which users have what roles in a particular run of a workflow
+ """
+ user = models.ForeignKey(User)
+ role = models.ForeignKey(Role)
+ workflowmanager = models.ForeignKey(WorkflowManager)
+
+class WorkflowEvent(models.Model):
+ """
+ Records what has happened and when in a particular run of a workflow. The
+ latest record for a WorkflowManager will indicate the current state.
+ """
+ workflowmanager = models.ForeignKey(WorkflowManager)
+ state = models.ForeignKey(State)
+ transition = models.ForeignKey(Transition, null=True)
+ participant = models.ForeignKey(Participant)
+ created_on = models.DateTimeField(auto_now_add=True)
1 views.py
@@ -0,0 +1 @@
+# Create your views here.

0 comments on commit 68f5f3a

Please sign in to comment.