Ross,

I understand your reasons for attaching approvals to the workflow.  I do it 
the other way because if a deliverable needs approval by six parties, 
putting six additional steps in the flow makes things a little cluttered. 
 If you add six people as performers of an approval step, does that mean 
that any one of the six can approve or must all of them?  

I very much like your workflow object table.

I'm still debating over whether to use auth to handle the roles.  I think 
auth needs a table called auth_role, actually a group of groups, to collect 
permissions.  It would be nice to have a 'has_role' decorator for it.

On Wednesday, May 16, 2012 2:33:35 PM UTC-4, Ross Peoples wrote:
>
> The data model I already have does things a bit differently, but I think 
> it accomplishes the same thing. I am in the process of writing all of the 
> support methods.
>
> This is my current data model:
>
> # workflow table
> db.define_table('workflow',
>     Field('name', length=50),
>     Field('is_template', 'boolean', default=False),
>     Field('created_by', db.auth_user, default=current_user),
>     Field('created_on', 'datetime', default=self.request.now),
>     Field('table_name', length=128),
>     Field('row_id', 'integer'),
>     Field('order_id', 'integer', default=self.ORDER_ID_STOP, 
> comment='Current position of the workflow'),
>     Field('priority', 'integer', requires=IS_INT_IN_RANGE(1, 9), 
> default=self.DEFAULT_PRIORITY)
> )
>
> # allow users / groups to "monitor" select workflows without needing to be 
> a part of the workflow
> # if workflow is template, this list is copied to the new workflow
> db.define_table('workflow_monitor',
>     Field('workflow_id', db.workflow),
>     Field('user_id', db.auth_user),
>     Field('group_id', db.auth_group),
>     Field('viewed', 'boolean', default=False) # once the monitor looks at 
> it, mark as viewed until another change happens
> )
>
> # comments can be attached to workflows so that users can voice questions 
> and concerns
> db.define_table('workflow_comment',
>     Field('workflow_id', db.workflow),
>     Field('user_id', db.auth_user, default=current_user),
>     Field('event_date', 'datetime', default=self.request.now),
>     Field('is_active', 'boolean', default=True, comment='Is the comment 
> waiting to be addressed'),
>     Field('comment', length=512, comment='The question, comment, or 
> concern'),
>     Field('response', length=512, comment='Response to the question, 
> comment, or concern')
> )
>
> # high-level list of goals for workflows. Users mark items as completed as 
> they complete the goals
> # if workflow is template, the checklist is copied to new workflow
> db.define_table('workflow_checklist',
>     Field('workflow_id', db.workflow),
>     Field('name', length=50),
>     Field('order_id', 'integer', comment='Ordering position of the item'),
>     Field('is_active', 'boolean', default=True, comment='Is the item 
> waiting to be addressed'),
>     Field('completed_by', db.auth_user),
>     Field('completed_at', 'datetime'),
>     Field('completed_note', length=512)
> )
>
> # workflow step table
> db.define_table('workflow_step',
>     Field('workflow_id', db.workflow),
>     Field('order_id', 'integer', comment='Ordering position of the step'),
>     Field('name', length=50),
>     Field('user_id', db.auth_user),
>     Field('group_id', db.auth_group),
>     Field('hours', 'decimal(10,2)', requires=IS_DECIMAL_IN_RANGE(0), 
> default=0, comment='Optional time limit in hours'),
>     Field('due_date', 'datetime', comment='Optional due date'),
>     Field('note', length=512, comment='Note on current state of the step') 
> # represents a stateful note (i.e. kickback reason). Can change at any time.
> )
>
> # audit tables
> db.define_table('workflow_event',
>     Field('workflow_id', 'integer'),
>     Field('table_name', length=128), # i.e. document, folder, etc
>     Field('row_id', 'integer'), # i.e. the ID of the document
>     Field('user_id', db.auth_user, default=current_user),
>     Field('event_date', 'datetime', default=self.request.now),
>     Field('action', length=10, default='update'), # could be: create, 
> update, delete
>     Field('field_name', length=128), # i.e order_id, name, hours, etc
>     Field('value_before', length=128), # None if create or delete
>     Field('value_after', length=128) # None if delete
> )
>
> db.define_table('workflow_step_event',
>     Field('workflow_id', 'integer'),
>     Field('step_id', 'integer'), # the ID of the workflow_step modified 
> (used to match up fields in batch changes)
>     Field('user_id', db.auth_user, default=current_user),
>     Field('event_date', 'datetime', default=self.request.now),
>     Field('action', length=10, default='update'), # could be: create, 
> update, delete
>     Field('field_name', length=128), # i.e order_id, name, hours, etc
>     Field('value_before', length=128), # None if create or delete
>     Field('value_after', length=128) # None if delete
> )
>
>
> As you can see, this model has quite a few of the features we discussed: 
> workflows, templates, monitors, comments, checklists, and auditing. It also 
> has triggers that applications can register (i.e. before_step_complete, 
> after_step_complete, etc) so applications can interact with the workflow 
> engine when an event is triggered.
>
> Workflows are copied from templates so that changes can be made to 
> templates and workflows without one affecting the other. This has worked 
> very well in my previous implementation. Tying monitors to templates 
> wouldn't work well like this. Attaching monitors to workflows themselves 
> also offers the advantage of giving the monitors a read/unread indicator 
> and when a workflow has been changed (i.e step completed), the indicator 
> turns back to "unread" so that way they keep up with workflows and not have 
> to remember if a workflow has changed since the last time they looked at it.
>
> My thinking on approvers is that they need to be part of the workflow. If 
> the workflow cannot progress until it has been approved, then it should be 
> a step in the workflow. If the approver finds something wrong, they can 
> reject their step (with notes as to why) and send it back to anyone in the 
> chain. Making approvals part of the workflow also has the advantage of 
> optionally setting a time limit for the approver to make a decision.
>
> I haven't made any attempts to add authorization functionality yet. I'm 
> thinking about using the web2py authorization system for this. You would 
> set up groups, add users to groups, and add workflow permissions to the 
> groups. For example:
>
> # allow group_id to create workflows on-the-fly
> auth.add_permission(group_id, 'create', 'workflow', 0)
>
> # allow group_id to load templates
> auth.add_permission(group_id, 'load_template', 'workflow', 0)
>
> # allow group_id to create templates
> auth.add_permission(group_id, 'create_template', 'workflow', 0)
>
> # give comment permission to group_id for workflow_id
> auth.add_permission(group_id, 'comment', 'workflow', workflow_id)
>
> # allow group_id to step-complete or reject steps for workflow_id
> auth.add_permission(group_id, 'flow', 'workflow', workflow_id)
>
> # allow group_id to modify workflow steps for workflow_id
> auth.add_permission(group_id, 'update', 'workflow', workflow_id)
>
> I would probably have the engine create a default set of groups and 
> permissions on first-run. For example, maybe groups called "Workflow 
> Monitor", "Workflow Participant", 'Workflow Creator", "Workflow Template 
> Creator". Each workflow would have permissions set for it that can be 
> predefined in templates (which are then copied to workflows when created).
>
>

Reply via email to