Index: openacs-4/contrib/packages/simulation/www/doc/workflow-extensions.html =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/packages/simulation/www/doc/Attic/workflow-extensions.html,v diff -u -r1.1 -r1.2 --- openacs-4/contrib/packages/simulation/www/doc/workflow-extensions.html 12 Nov 2003 13:46:11 -0000 1.1 +++ openacs-4/contrib/packages/simulation/www/doc/workflow-extensions.html 17 Nov 2003 14:46:58 -0000 1.2 @@ -34,10 +34,10 @@

The timer will always be of the form "This action will automatically - execute x number of seconds after it becomes enabled". If it is - later un-enabled (disabled) because another action (e.g. a vote - action in the second use casae above) was executed, then the timer - will be reset. If the action later becomes enabled, the timer will start + execute x amount of time after it becomes enabled". If it is later + un-enabled (disabled) because another action (e.g. a vote action in + the second use casae above) was executed, then the timer will be + reset. If the action later becomes enabled, the timer will start anew.

@@ -51,71 +51,80 @@ changes.

-

Extending workflow_actions:

+

Extending workflow_actions

-create table workflow_case_timed_actions(
+create table workflow_actions(
     ...
     -- The number of seconds after having become enabled the action
     -- will automatically execute
-    timeout_seconds         integer
+    timeout                 interval
     ...
 );
 
-

The table:

+

+ DESIGN NOTE: The 'interval' datatype is not supported in + Oracle. +

+

The Enabled Actions Table

+
-create table workflow_case_timed_actions(
+create table workflow_case_enabled_actions(
     case_id                 integer
-                            constraint wf_case_time_act_case_id_nn
+                            constraint wf_case_enbl_act_case_id_nn
                             not null
-                            constraint wf_case_time_act_case_id_fk
+                            constraint wf_case_enbl_act_case_id_fk
                             references workflow_cases(case_id)
                             on delete cascade,
     action_id               integer
-                            constraint wf_case_time_act_action_id_nn
+                            constraint wf_case_enbl_act_action_id_nn
                             not null
-                            constraint wf_case_time_act_action_id_fk
+                            constraint wf_case_enbl_act_action_id_fk
                             references workflow_actions(action_id)
                             on delete cascade,
-    -- the timestamp when this action fires
-    fire_timestamp          timestamp
-                            constraint wf_case_time_act_timeout_nn
+    -- the timestamp when this action will fires
+    execution_time          timestamptz
+                            constraint wf_case_enbl_act_timeout_nn
                             not null,
-    constraint workflow_case_timed_actions_pk
+    constraint workflow_case_enabled_actions_pk
     primary key (case_id, action_id)
 );
 
-

The logic:

+

The Logic

After executing an action, workflow::case::action::execute will:

  1. - Delete all actions from worklfow_case_timed_actions which are no longer enabled. + Delete all actions from worklfow_case_enabled_actions which are no longer enabled.
  2. + If the timeout is zero, execute immediately. +
  3. +
  4. Insert a row for all enabled actions with timeouts which are not - already in workflow_case_timed_actions, with + already in workflow_case_enabled_actions, with fire_timestamp = current_timestamp + workflow_actions.timeout_seconds .
  5. -
  6. - Run the sweeper immediately, so if timeout_seconds is - zero (automatic action), the action will execute right away, and - not at the next sweep. -
-

The sweeper

+

+ NOTE: We need to keep running, so if another automatic action + becomes enabled after this action fires, they'll fire as well. +

+

The Sweeper

+

The sweeper will find rows in - workflow_case_timed_actions with fire_timetsamp < - current_timestamp, order by fire_timstamp, and execute them. + workflow_case_enabled_actions with fire_timetsamp + < current_timestamp, ordered by fire_timstamp, and execute + them.

@@ -139,7 +148,7 @@

-

Triggers/Events/Related Actions/Find Good Term

+

Hierarchical Workflows

Requirements

@@ -167,50 +173,488 @@ -

Relating Tasks

+

Design

+ -

IS THIS DIFFERENT FROM THE TRIGGER/EVENT/WHATNOT STUFF ABOVE? +

Data Model

-

Requirements

+
+create table workflow_actions(
+  ...
+  child_workflow            integer
+                            constraint wf_action_child_wf_fk
+                            references workflows(workflow_id),
+  ...
+);
 
+create table workflow_fsm_states(
+  ...
+  -- does this state imply that the case is completed?
+  complete_p              boolean,
+  ...
+);
+
+create table workflow_action_fsm_output_map(
+  action_id               integer
+                          not null
+                          references workflow_actions(action_id)
+                          on delete cascade,
+  acs_sc_impl_id          integer
+                          not null
+                          references acs_sc_impls(impl_id)
+                          on delete cascade,
+  output_value            varchar(4000),
+  new_state               integer
+                          references workflow_fsm_states
+);
+
+create table workflow_action_child_role_map(
+  parent_action_id          integer
+                            constraint wf_act_chid_rl_map_prnt_act_fk
+                            references workflow_actions(action_id),
+  parent_role               integer
+                            constraint wf_act_chid_rl_map_prnt_rl_fk
+                            references workflow_roles(role_id),
+  child_role                integer
+                            constraint wf_act_chid_rl_map_chld_rl_fk
+                            references workflow_roles(role_id),
+  mapping_type              char(40)
+                            constraint wf_act_chid_rl_map_type_ck
+                            check (mapping_type in 
+                                ('per_role','per_member','per_user'))
+);
+
+
+create table workflow_cases(
+  ...
+  state                      char(40)
+                             constraint workflow_cases_state_ck
+                             check (state in ('active', 'completed',
+                             'closed', 'canceled', 'suspended'))
+                             default 'active',
+  suspended_until            timestamptz,
+  parent_enabled_action_id   integer
+                             constraint workflow_cases_parent_fk
+                             references workflow_case_enabled_actions(enabled_action_id)
+);
+
+create table workflow_case_enabled_actions(
+    enabled_action_id       integer
+                            constraint wf_case_enbl_act_case_id_pk
+                            primary key,
+    case_id                 integer
+                            constraint wf_case_enbl_act_case_id_nn
+                            not null
+                            constraint wf_case_enbl_act_case_id_fk
+                            references workflow_cases(case_id)
+                            on delete cascade,
+    action_id               integer
+                            constraint wf_case_enbl_act_action_id_nn
+                            not null
+                            constraint wf_case_enbl_act_action_id_fk
+                            references workflow_actions(action_id)
+                            on delete cascade,
+    enabled_state           char(40)
+                            constraint wf_case_enbl_act_state_ck
+                            check (enabled_state in ('enabled','completed','canceled','refused')),
+    -- the timestamp when this action automatically fires
+    fire_timestamp          timestamp
+                            constraint wf_case_enbl_act_timeout_nn
+                            not null,
+    constraint wf_case_ena_act_case_act_un
+    primary key (case_id, action_id)
+);
+
+
+ +

Callback Types

+ + + +

Callback Output

+

- Some process is triggered by typically a timer on an action being - enabled. + The callbacks returning 'output' above must enumerate all the values + they can possible output (similar contruct to GetObjectType + operation on other current workflow service contracts), and the + callback itself must return one of those possible values.

+

+ The workflow engine will then allow the workflow designer to map + these possible output values of the callback to new states, in the + case of an FSM, or similar relevant state changes for other models. +

+

Enabled Action Logic

-

Relating Workflows/Cases

+

+ Executed when an action which was previously not enabled becomes enabled. +

-

Requirements

+
    +
  1. + If the action has a timeout of 0, then execute the action and quit. +
  2. +
  3. + Insert a row into workflow_case_enabled_actions. +
  4. +
  5. + If the action has non-null timeout > 0, then the row will have a + execution_time of current_timestamp + timeout. +
  6. +
  7. + If the action has non-null child_workflow, create child cases. For + each role which has a mapping_type of 'per_member' or 'per_user', + create one case per member/user of that role. If more roles have + per_member/per_user setting, then the cartesian product of child + cases are created (DESIGN QUESTION: Would this ever be relevant?) +
  8. +
  9. + If there is any ActionEnabled callback, execute that (only the + first, if multiple exists), and use the workflow_fsm_output_map to + determine which new state to bump the workflow to, if any. +
  10. +
+

Un-Enabled Action Logic

+

- Use cases: + Executed when an action which was previously enabled is no longer + enabled, because the workflow's state was changed by some other + action.

+
    +
  1. + If the action has any child cases, these will be marked canceled. +
  2. +
+ +

Child Case State Changed Logic

+ +

+ We execute the OnChildCaseStateChange callback, if any. This gets to + determine whether the parent action is now complete and should fire. +

+ +

+ We provide a default implementation, which simply checks if the + child cases are in the 'complete' state, and if so, fires. +

+ +

+ NOTE: What do we do if any of the child cases are canceled? Consider + the complete and move on with the parent workflow? Cancel the parent + workflow? +

+ +

+ NOTE: Should we provide this as internal workflow logic or as a + default callback implementation? If we leave this as a service + contract with a default implementation, then applications can + customize. But would that ever be relevant? Maybe this callback is + never needed. +

+ +

On Fire Logic

+ +

+ When the action finally fires. +

+ +

+ If there's any OnFire callback defined, we execute this. +

+ +

+ If the callback has output values defined, we use the mappings in + workflow_action_fsm_output_map to determine which state to + move to. +

+ +

+ After firing, we execute the SideEffect callbacks and send off + notifications. +

+ +

+ DESIGN QUESTION: How do we handle notifications for child cases? We + should consider the child case part of the parent in terms of + notifications, so when a child action executes, we notify those who + have requested notifications on the parent. And when the last child + case completes, which will also complete the parent action, we + should avoid sending out duplicate notifications. How? +

+ +

Case State

+ +

+ Cases can be active, complete, suspended, or canceled. +

+ +

+ They start out as active. For FSMs, when they hit a state with + complete_p = t, the case is moved to 'complete'. +

+ +

+ Users can choose to cancel or suspend a case. When suspending, they + can type in a date, on which the case will spring back to 'active' + life. +

+ +

+ When a parent worfklow completes an action with a sub-workflow, the + child cases that are 'completed' are marked 'closed', and the child + cases that are 'active' are marked 'canceled'. +

+ +

+ The difference between 'completed' and 'closed' is that completed + does not prevent the workflow from continuing (e.g. bug-tracker + 'closed' state doesn't mean that it cannot be reopened), whereas a + closed case cannot be reactivarted (terminology confusion alert!). +

+ + + + + +

Appendix: Resolution Code

+ + +

Appendix: TIP Voting Process

+ +
+TIP Master Workflow
+  Model = FSM
+  Roles
+    Submitter
+    Voter
+  States
+    Proposed
+    Voting
+    Withdrawn
+    Approved
+    Rejected
+  Actions
+    Propose
+      Initial action
+      New state = Proposed
+    Vote
+      Enabled in state = Proposed
+      Role = Voter
+      Sub-workflow = Individual Vote
+        In progress state = Voting
+        Sub-role Voter = pparent role Voter
+          One sub-case per user in the Voter role
+      New state = Approved | Rejected
+      Logic = 
+        0 Rejects + > 0 Approvals = Approved
+        2/3rds Approvals => Approved
+        Otherwise => Rejected
+    Withdraw
+      Enabled in state = Proposed, Voting
+      Role = Submitter
+      New state = Withdrawn
+
+TIP Individual Vote Workflow
+  Model = FSM
+  Roles
+    Voter
+  States
+    Open
+    Approved
+    Rejected
+    Abstained
+  Actions
+    Open
+      Initial action
+      New state = Open
+    Approve
+      Enabled in state = Open
+      Role = Voter
+      New state = Approved
+    Reject
+      Enabled in state = Open
+      Role = Voter
+      New state = Rejected
+    Abstain
+      Enabled in state = Open
+      Role = Voter
+      New state = Abstained
+    No Vote
+      Enabled in state = Open
+      Timeout = 7 days
+      New state = Abstained
+
+ +

Appendix: Leiden's Example Workflow

+ +
+Leiden Master Workflow
+  Model = Dependency
+  Roles
+    Client 1
+    Lawyer 1
+    Partner 1
+    Secretary 1
+    Client 2
+    Lawyer 2
+    Partner 2
+    Secretary 2
+    Library
+  Actions
+    L1 Info Request From C2
+    L1 Info Request From L2
+      Dependent on = L1 Info Request From C2
+    L1 Info Request From Library
+    
+    L1 Draft With S1
+      Dependent on = L1 Info Request From L2, L1 Info Request From Library
+    L1 Send Document
+      Dependent on = L1 Draft With S1
+
+    P1 Intervenes With L1
+
+    L2 Info Request From C1
+    L2 Info Request From L1
+      Dependent on = L2 Info Request From C1
+    L2 Info Request From Library
+    
+    L2 Draft With S2
+      Dependent on = L2 Info Request From L1, L2 Info Request From Library
+    L2 Send Document
+      Dependent on = L2 Draft With S2
+
+    P2 Intervenes With L2
+
+    Done
+      Dependent on = L1 Send Document, L2 Send Document
+
+AskInfo-GiveInfo Loop Workflow
+  Roles
+    Informer
+    Recipient
+  States
+    Asked
+    Given
+  Actions
+    Ask Info
+      Initial action
+      New State = Asked
+    Give Info
+      Enabled in state = Asked
+      Role = Informer
+      New state = Given
+
+ + +