petri_flow 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +34 -0
- data/app/assets/config/wf_manifest.js +2 -0
- data/app/assets/javascripts/wf/application.js +4 -0
- data/app/assets/stylesheets/wf/application.scss +2 -0
- data/app/assets/stylesheets/wf/arcs.css +4 -0
- data/app/assets/stylesheets/wf/cases.css +4 -0
- data/app/assets/stylesheets/wf/comments.css +4 -0
- data/app/assets/stylesheets/wf/fields.css +4 -0
- data/app/assets/stylesheets/wf/forms.css +4 -0
- data/app/assets/stylesheets/wf/guards.css +4 -0
- data/app/assets/stylesheets/wf/places.css +4 -0
- data/app/assets/stylesheets/wf/transitions.css +4 -0
- data/app/assets/stylesheets/wf/workflows.css +4 -0
- data/app/assets/stylesheets/wf/workitem_assignments.css +4 -0
- data/app/assets/stylesheets/wf/workitems.css +4 -0
- data/app/controllers/wf/application_controller.rb +14 -0
- data/app/controllers/wf/arcs_controller.rb +61 -0
- data/app/controllers/wf/cases_controller.rb +48 -0
- data/app/controllers/wf/comments_controller.rb +30 -0
- data/app/controllers/wf/fields_controller.rb +54 -0
- data/app/controllers/wf/forms_controller.rb +59 -0
- data/app/controllers/wf/guards_controller.rb +58 -0
- data/app/controllers/wf/places_controller.rb +53 -0
- data/app/controllers/wf/transitions_controller.rb +69 -0
- data/app/controllers/wf/workflows_controller.rb +59 -0
- data/app/controllers/wf/workitem_assignments_controller.rb +30 -0
- data/app/controllers/wf/workitems_controller.rb +79 -0
- data/app/helpers/wf/application_helper.rb +6 -0
- data/app/helpers/wf/arcs_helper.rb +6 -0
- data/app/helpers/wf/cases_helper.rb +6 -0
- data/app/helpers/wf/comments_helper.rb +6 -0
- data/app/helpers/wf/fields_helper.rb +6 -0
- data/app/helpers/wf/forms_helper.rb +6 -0
- data/app/helpers/wf/guards_helper.rb +6 -0
- data/app/helpers/wf/places_helper.rb +6 -0
- data/app/helpers/wf/transitions_helper.rb +6 -0
- data/app/helpers/wf/workflows_helper.rb +6 -0
- data/app/helpers/wf/workitem_assignments_helper.rb +6 -0
- data/app/helpers/wf/workitems_helper.rb +6 -0
- data/app/jobs/wf/application_job.rb +6 -0
- data/app/jobs/wf/fire_timed_workitem_job.rb +15 -0
- data/app/mailers/wf/application_mailer.rb +8 -0
- data/app/models/wf/application_record.rb +7 -0
- data/app/models/wf/arc.rb +42 -0
- data/app/models/wf/callbacks/assignment_default.rb +12 -0
- data/app/models/wf/callbacks/deadline_default.rb +11 -0
- data/app/models/wf/callbacks/enable_default.rb +11 -0
- data/app/models/wf/callbacks/fire_default.rb +11 -0
- data/app/models/wf/callbacks/hold_timeout_default.rb +11 -0
- data/app/models/wf/callbacks/notification_default.rb +11 -0
- data/app/models/wf/callbacks/time_default.rb +11 -0
- data/app/models/wf/callbacks/unassignment_default.rb +11 -0
- data/app/models/wf/case.rb +44 -0
- data/app/models/wf/case_assignment.rb +22 -0
- data/app/models/wf/case_command/add_comment.rb +17 -0
- data/app/models/wf/case_command/add_manual_assignment.rb +17 -0
- data/app/models/wf/case_command/add_token.rb +20 -0
- data/app/models/wf/case_command/add_workitem_assignment.rb +35 -0
- data/app/models/wf/case_command/begin_workitem_action.rb +33 -0
- data/app/models/wf/case_command/cancel.rb +17 -0
- data/app/models/wf/case_command/cancel_workitem.rb +20 -0
- data/app/models/wf/case_command/clear_manual_assignments.rb +16 -0
- data/app/models/wf/case_command/clear_workitem_assignments.rb +20 -0
- data/app/models/wf/case_command/consume_token.rb +23 -0
- data/app/models/wf/case_command/enable_transitions.rb +33 -0
- data/app/models/wf/case_command/end_workitem_action.rb +25 -0
- data/app/models/wf/case_command/finish_workitem.rb +18 -0
- data/app/models/wf/case_command/finished_p.rb +28 -0
- data/app/models/wf/case_command/fire_message_transition.rb +20 -0
- data/app/models/wf/case_command/fire_transition_internal.rb +41 -0
- data/app/models/wf/case_command/lock_token.rb +21 -0
- data/app/models/wf/case_command/new.rb +17 -0
- data/app/models/wf/case_command/release_token.rb +20 -0
- data/app/models/wf/case_command/remove_manual_assignment.rb +17 -0
- data/app/models/wf/case_command/remove_workitem_assignment.rb +24 -0
- data/app/models/wf/case_command/resume.rb +17 -0
- data/app/models/wf/case_command/set_workitem_assignments.rb +32 -0
- data/app/models/wf/case_command/start_case.rb +19 -0
- data/app/models/wf/case_command/start_workitem.rb +26 -0
- data/app/models/wf/case_command/suspend.rb +17 -0
- data/app/models/wf/case_command/sweep_automatic_transitions.rb +31 -0
- data/app/models/wf/case_command/sweep_timed_transitions.rb +16 -0
- data/app/models/wf/case_command/workitem_action.rb +20 -0
- data/app/models/wf/comment.rb +21 -0
- data/app/models/wf/demo_target.rb +18 -0
- data/app/models/wf/entry.rb +33 -0
- data/app/models/wf/field.rb +53 -0
- data/app/models/wf/field_value.rb +30 -0
- data/app/models/wf/form.rb +18 -0
- data/app/models/wf/group.rb +21 -0
- data/app/models/wf/guard.rb +73 -0
- data/app/models/wf/party.rb +22 -0
- data/app/models/wf/place.rb +28 -0
- data/app/models/wf/token.rb +37 -0
- data/app/models/wf/transition.rb +49 -0
- data/app/models/wf/transition_static_assignment.rb +21 -0
- data/app/models/wf/user.rb +26 -0
- data/app/models/wf/workflow.rb +182 -0
- data/app/models/wf/workitem.rb +78 -0
- data/app/models/wf/workitem_assignment.rb +19 -0
- data/app/views/layouts/wf/_alert.html.erb +4 -0
- data/app/views/layouts/wf/_footer.html.erb +13 -0
- data/app/views/layouts/wf/_nav.html.erb +13 -0
- data/app/views/layouts/wf/_notice.html.erb +4 -0
- data/app/views/layouts/wf/application.html.erb +34 -0
- data/app/views/wf/arcs/_form.html.erb +40 -0
- data/app/views/wf/arcs/edit.html.erb +1 -0
- data/app/views/wf/arcs/new.html.erb +1 -0
- data/app/views/wf/arcs/show.html.erb +67 -0
- data/app/views/wf/cases/_form.html.erb +27 -0
- data/app/views/wf/cases/index.html.erb +35 -0
- data/app/views/wf/cases/new.html.erb +1 -0
- data/app/views/wf/cases/show.html.erb +113 -0
- data/app/views/wf/comments/new.html.erb +27 -0
- data/app/views/wf/fields/_form.html.erb +42 -0
- data/app/views/wf/fields/edit.html.erb +1 -0
- data/app/views/wf/fields/new.html.erb +1 -0
- data/app/views/wf/forms/_form.html.erb +32 -0
- data/app/views/wf/forms/edit.html.erb +1 -0
- data/app/views/wf/forms/index.html.erb +32 -0
- data/app/views/wf/forms/new.html.erb +1 -0
- data/app/views/wf/forms/show.html.erb +53 -0
- data/app/views/wf/guards/_form.html.erb +43 -0
- data/app/views/wf/guards/edit.html.erb +1 -0
- data/app/views/wf/guards/new.html.erb +1 -0
- data/app/views/wf/places/_form.html.erb +43 -0
- data/app/views/wf/places/edit.html.erb +1 -0
- data/app/views/wf/places/new.html.erb +1 -0
- data/app/views/wf/transitions/_form.html.erb +92 -0
- data/app/views/wf/transitions/edit.html.erb +1 -0
- data/app/views/wf/transitions/new.html.erb +1 -0
- data/app/views/wf/workflows/_form.html.erb +32 -0
- data/app/views/wf/workflows/edit.html.erb +1 -0
- data/app/views/wf/workflows/index.html.erb +38 -0
- data/app/views/wf/workflows/new.html.erb +1 -0
- data/app/views/wf/workflows/show.html.erb +177 -0
- data/app/views/wf/workitem_assignments/new.html.erb +27 -0
- data/app/views/wf/workitems/index.html.erb +68 -0
- data/app/views/wf/workitems/pre_finish.html.erb +33 -0
- data/app/views/wf/workitems/show.html.erb +211 -0
- data/config/routes.rb +30 -0
- data/db/migrate/20200130201043_init.rb +226 -0
- data/db/migrate/20200130201641_init_some_data.rb +15 -0
- data/db/migrate/20200131200455_create_wf_entries.rb +19 -0
- data/db/migrate/20200201001543_add_target_field_name_for_guard.rb +7 -0
- data/lib/tasks/wf_tasks.rake +5 -0
- data/lib/wf/engine.rb +10 -0
- data/lib/wf/version.rb +5 -0
- data/lib/wf.rb +7 -0
- metadata +312 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# == Schema Information
|
4
|
+
#
|
5
|
+
# Table name: wf_field_values
|
6
|
+
#
|
7
|
+
# id :integer not null, primary key
|
8
|
+
# workflow_id :integer
|
9
|
+
# transition_id :integer
|
10
|
+
# form_id :integer
|
11
|
+
# field_id :integer
|
12
|
+
# value :text
|
13
|
+
# created_at :datetime not null
|
14
|
+
# updated_at :datetime not null
|
15
|
+
# entry_id :integer
|
16
|
+
#
|
17
|
+
|
18
|
+
module Wf
|
19
|
+
class FieldValue < ApplicationRecord
|
20
|
+
belongs_to :workflow, optional: true
|
21
|
+
belongs_to :transition, optional: true
|
22
|
+
belongs_to :form
|
23
|
+
belongs_to :field
|
24
|
+
belongs_to :entry
|
25
|
+
|
26
|
+
def value_after_cast
|
27
|
+
field.type_for_cast.cast(value)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# == Schema Information
|
4
|
+
#
|
5
|
+
# Table name: wf_forms
|
6
|
+
#
|
7
|
+
# id :integer not null, primary key
|
8
|
+
# name :string
|
9
|
+
# description :text
|
10
|
+
# created_at :datetime not null
|
11
|
+
# updated_at :datetime not null
|
12
|
+
#
|
13
|
+
|
14
|
+
module Wf
|
15
|
+
class Form < ApplicationRecord
|
16
|
+
has_many :fields, dependent: :destroy
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# == Schema Information
|
4
|
+
#
|
5
|
+
# Table name: wf_groups
|
6
|
+
#
|
7
|
+
# id :integer not null, primary key
|
8
|
+
# name :string
|
9
|
+
# created_at :datetime not null
|
10
|
+
# updated_at :datetime not null
|
11
|
+
#
|
12
|
+
|
13
|
+
module Wf
|
14
|
+
class Group < ApplicationRecord
|
15
|
+
has_many :users
|
16
|
+
has_one :party, as: :partable
|
17
|
+
after_create do
|
18
|
+
create_party(party_name: name)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# == Schema Information
|
4
|
+
#
|
5
|
+
# Table name: wf_guards
|
6
|
+
#
|
7
|
+
# id :integer not null, primary key
|
8
|
+
# arc_id :integer
|
9
|
+
# workflow_id :integer
|
10
|
+
# fieldable_type :string
|
11
|
+
# fieldable_id :string
|
12
|
+
# op :string
|
13
|
+
# value :string
|
14
|
+
# exp :string
|
15
|
+
# created_at :datetime not null
|
16
|
+
# updated_at :datetime not null
|
17
|
+
# target_attr_name :string
|
18
|
+
#
|
19
|
+
|
20
|
+
module Wf
|
21
|
+
class Guard < ApplicationRecord
|
22
|
+
belongs_to :workflow
|
23
|
+
belongs_to :arc, touch: true, counter_cache: true
|
24
|
+
belongs_to :fieldable, polymorphic: true, optional: true
|
25
|
+
|
26
|
+
before_validation do
|
27
|
+
self.workflow = arc.workflow
|
28
|
+
end
|
29
|
+
|
30
|
+
OP = %w[
|
31
|
+
=
|
32
|
+
>
|
33
|
+
<
|
34
|
+
>=
|
35
|
+
<=
|
36
|
+
is_empty
|
37
|
+
].freeze
|
38
|
+
|
39
|
+
def value_after_cast
|
40
|
+
field = fieldable
|
41
|
+
if fieldable
|
42
|
+
fieldable.type_for_cast.cast(value)
|
43
|
+
else
|
44
|
+
# TODO
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def pass?(entry)
|
49
|
+
fv = entry.field_values.where(field_id: fieldable_id).first
|
50
|
+
return unless fv
|
51
|
+
|
52
|
+
if op == "="
|
53
|
+
fv.value_after_cast == value_after_cast
|
54
|
+
elsif op == ">"
|
55
|
+
fv.value_after_cast > value_after_cast
|
56
|
+
elsif op == "<"
|
57
|
+
fv.value_after_cast < value_after_cast
|
58
|
+
elsif op == ">="
|
59
|
+
fv.value_after_cast >= value_after_cast
|
60
|
+
elsif op == "<="
|
61
|
+
fv.value_after_cast <= value_after_cast
|
62
|
+
elsif op == "is_empty"
|
63
|
+
fv.value_after_cast.blank?
|
64
|
+
else
|
65
|
+
false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def inspect
|
70
|
+
%(#{fieldable&.form&.name}.#{fieldable&.name} #{op} #{value})
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# == Schema Information
|
4
|
+
#
|
5
|
+
# Table name: wf_parties
|
6
|
+
#
|
7
|
+
# id :integer not null, primary key
|
8
|
+
# partable_type :string
|
9
|
+
# partable_id :string
|
10
|
+
# party_name :string
|
11
|
+
# created_at :datetime not null
|
12
|
+
# updated_at :datetime not null
|
13
|
+
#
|
14
|
+
|
15
|
+
module Wf
|
16
|
+
class Party < ApplicationRecord
|
17
|
+
# TODO: use acts_as_partable for sync group or role or user to party
|
18
|
+
belongs_to :partable, polymorphic: true
|
19
|
+
has_many :transition_static_assignments
|
20
|
+
has_many :workitem_assignments
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# == Schema Information
|
4
|
+
#
|
5
|
+
# Table name: wf_places
|
6
|
+
#
|
7
|
+
# id :integer not null, primary key
|
8
|
+
# workflow_id :integer
|
9
|
+
# name :string
|
10
|
+
# description :text
|
11
|
+
# sort_order :integer default("0")
|
12
|
+
# place_type :integer default("0")
|
13
|
+
# created_at :datetime not null
|
14
|
+
# updated_at :datetime not null
|
15
|
+
#
|
16
|
+
|
17
|
+
module Wf
|
18
|
+
class Place < ApplicationRecord
|
19
|
+
belongs_to :workflow, touch: true
|
20
|
+
has_many :arcs
|
21
|
+
has_many :tokens
|
22
|
+
enum place_type: {
|
23
|
+
start: 0,
|
24
|
+
normal: 1,
|
25
|
+
end: 2
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# == Schema Information
|
4
|
+
#
|
5
|
+
# Table name: wf_tokens
|
6
|
+
#
|
7
|
+
# id :integer not null, primary key
|
8
|
+
# workflow_id :integer
|
9
|
+
# case_id :integer
|
10
|
+
# targetable_type :string
|
11
|
+
# targetable_id :string
|
12
|
+
# place_id :integer
|
13
|
+
# state :integer default("0")
|
14
|
+
# locked_workitem_id :integer
|
15
|
+
# produced_at :datetime
|
16
|
+
# locked_at :datetime
|
17
|
+
# canceled_at :datetime
|
18
|
+
# consumed_at :datetime
|
19
|
+
# created_at :datetime not null
|
20
|
+
# updated_at :datetime not null
|
21
|
+
#
|
22
|
+
|
23
|
+
module Wf
|
24
|
+
class Token < ApplicationRecord
|
25
|
+
belongs_to :workflow
|
26
|
+
belongs_to :case
|
27
|
+
belongs_to :place
|
28
|
+
belongs_to :locked_workitem, class_name: "Wf::Workitem", optional: true
|
29
|
+
|
30
|
+
enum state: {
|
31
|
+
free: 0,
|
32
|
+
locked: 1,
|
33
|
+
canceled: 2,
|
34
|
+
consumed: 3
|
35
|
+
}
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# == Schema Information
|
4
|
+
#
|
5
|
+
# Table name: wf_transitions
|
6
|
+
#
|
7
|
+
# id :integer not null, primary key
|
8
|
+
# name :string
|
9
|
+
# description :text
|
10
|
+
# workflow_id :integer
|
11
|
+
# sort_order :integer default("0")
|
12
|
+
# trigger_limit :integer
|
13
|
+
# trigger_type :integer default("0")
|
14
|
+
# created_at :datetime not null
|
15
|
+
# updated_at :datetime not null
|
16
|
+
# form_id :integer
|
17
|
+
# enable_callback :string default("Wf::Callbacks::EnableDefault")
|
18
|
+
# fire_callback :string default("Wf::Callbacks::FireDefault")
|
19
|
+
# notification_callback :string default("Wf::Callbacks::NotificationDefault")
|
20
|
+
# time_callback :string default("Wf::Callbacks::TimeDefault")
|
21
|
+
# deadline_callback :string default("Wf::Callbacks::DeadlineDefault")
|
22
|
+
# hold_timeout_callback :string default("Wf::Callbacks::HoldTimeoutDefault")
|
23
|
+
# assignment_callback :string default("Wf::Callbacks::AssignmentDefault")
|
24
|
+
# unassignment_callback :string default("Wf::Callbacks::UnassignmentDefault")
|
25
|
+
#
|
26
|
+
|
27
|
+
module Wf
|
28
|
+
class Transition < ApplicationRecord
|
29
|
+
belongs_to :workflow, touch: true
|
30
|
+
has_many :arcs
|
31
|
+
has_many :transition_static_assignments
|
32
|
+
has_many :static_parties, through: :transition_static_assignments, source: "party"
|
33
|
+
has_many :workitems
|
34
|
+
belongs_to :form, optional: true
|
35
|
+
|
36
|
+
enum trigger_type: {
|
37
|
+
user: 0,
|
38
|
+
automatic: 1,
|
39
|
+
message: 2,
|
40
|
+
time: 3
|
41
|
+
}
|
42
|
+
|
43
|
+
def explicit_or_split?
|
44
|
+
arcs.out.sum(:guards_count) >= 1
|
45
|
+
end
|
46
|
+
|
47
|
+
validates :name, presence: true
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# == Schema Information
|
4
|
+
#
|
5
|
+
# Table name: wf_transition_static_assignments
|
6
|
+
#
|
7
|
+
# id :integer not null, primary key
|
8
|
+
# party_id :integer
|
9
|
+
# transition_id :integer
|
10
|
+
# workflow_id :integer
|
11
|
+
# created_at :datetime not null
|
12
|
+
# updated_at :datetime not null
|
13
|
+
#
|
14
|
+
|
15
|
+
module Wf
|
16
|
+
class TransitionStaticAssignment < ApplicationRecord
|
17
|
+
belongs_to :workflow
|
18
|
+
belongs_to :transition
|
19
|
+
belongs_to :party
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# == Schema Information
|
4
|
+
#
|
5
|
+
# Table name: wf_users
|
6
|
+
#
|
7
|
+
# id :integer not null, primary key
|
8
|
+
# name :string
|
9
|
+
# created_at :datetime not null
|
10
|
+
# updated_at :datetime not null
|
11
|
+
# group_id :integer
|
12
|
+
#
|
13
|
+
|
14
|
+
module Wf
|
15
|
+
class User < ApplicationRecord
|
16
|
+
belongs_to :group, optional: true
|
17
|
+
has_one :party, as: :partable
|
18
|
+
|
19
|
+
# NOTICE: group or user or role all has_many users
|
20
|
+
has_many :users, foreign_key: :id
|
21
|
+
|
22
|
+
after_create do
|
23
|
+
create_party(party_name: name)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# == Schema Information
|
4
|
+
#
|
5
|
+
# Table name: wf_workflows
|
6
|
+
#
|
7
|
+
# id :integer not null, primary key
|
8
|
+
# name :string
|
9
|
+
# description :text
|
10
|
+
# is_valid :boolean default("false")
|
11
|
+
# creator_id :string
|
12
|
+
# error_msg :text
|
13
|
+
# created_at :datetime not null
|
14
|
+
# updated_at :datetime not null
|
15
|
+
#
|
16
|
+
|
17
|
+
module Wf
|
18
|
+
class Workflow < ApplicationRecord
|
19
|
+
has_many :places, dependent: :destroy
|
20
|
+
has_many :transitions, dependent: :destroy
|
21
|
+
has_many :arcs, dependent: :destroy
|
22
|
+
has_many :transition_static_assignments
|
23
|
+
has_many :cases
|
24
|
+
has_many :workitems
|
25
|
+
has_many :tokens
|
26
|
+
|
27
|
+
validates :name, presence: true
|
28
|
+
|
29
|
+
# TODO: move to config
|
30
|
+
def self.enable_callbacks
|
31
|
+
[Wf::Callbacks::EnableDefault]
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.fire_callbacks
|
35
|
+
[Wf::Callbacks::FireDefault]
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.assignment_callbacks
|
39
|
+
[Wf::Callbacks::AssignmentDefault]
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.unassignment_callbacks
|
43
|
+
[Wf::Callbacks::UnassignmentDefault]
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.notification_callbacks
|
47
|
+
[Wf::Callbacks::NotificationDefault]
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.deadline_callbacks
|
51
|
+
[Wf::Callbacks::DeadlineDefault]
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.time_callbacks
|
55
|
+
[Wf::Callbacks::TimeDefault]
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.hold_timeout_callbacks
|
59
|
+
[Wf::Callbacks::HoldTimeoutDefault]
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.user_class
|
63
|
+
::Wf::User
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.org_classes
|
67
|
+
{
|
68
|
+
group: ::Wf::Group
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
after_save do
|
73
|
+
do_validate!
|
74
|
+
end
|
75
|
+
|
76
|
+
after_touch do
|
77
|
+
do_validate!
|
78
|
+
end
|
79
|
+
|
80
|
+
# TODO: can from start place to end place
|
81
|
+
def do_validate!
|
82
|
+
msgs = []
|
83
|
+
msgs << "must have start place" if places.start.blank?
|
84
|
+
msgs << "must have only one start place" if places.start.count > 1
|
85
|
+
msgs << "must have end place" if places.end.blank?
|
86
|
+
msgs << "must have only one end place" if places.end.count > 1
|
87
|
+
if msgs.present?
|
88
|
+
update_columns(is_valid: false, error_msg: msgs.join("\n"))
|
89
|
+
else
|
90
|
+
update_columns(is_valid: true, error_msg: "")
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def to_graph(wf_case = nil)
|
95
|
+
graph = GraphViz.new(name, type: :digraph, rankdir: "LR", splines: true, ratio: :auto)
|
96
|
+
free_token_places = if wf_case
|
97
|
+
wf_case.tokens.free.map(&:place_id)
|
98
|
+
else
|
99
|
+
[]
|
100
|
+
end
|
101
|
+
pg_mapping = {}
|
102
|
+
places.order("place_type ASC").each do |p|
|
103
|
+
if p.start?
|
104
|
+
fillcolor = :yellow
|
105
|
+
shape = :doublecircle
|
106
|
+
elsif p.end?
|
107
|
+
fillcolor = :green
|
108
|
+
shape = :doublecircle
|
109
|
+
else
|
110
|
+
fillcolor = :lightpink
|
111
|
+
shape = :circle
|
112
|
+
end
|
113
|
+
|
114
|
+
token_count = free_token_places.count(p.id)
|
115
|
+
if token_count >= 1
|
116
|
+
label = "•"
|
117
|
+
xlabel = nil
|
118
|
+
fontsize = 25
|
119
|
+
else
|
120
|
+
label = p.name
|
121
|
+
xlabel = nil
|
122
|
+
end
|
123
|
+
|
124
|
+
pg = graph.add_nodes(p.name,
|
125
|
+
label: label,
|
126
|
+
xlabel: xlabel,
|
127
|
+
shape: shape,
|
128
|
+
fixedsize: true,
|
129
|
+
style: :filled,
|
130
|
+
fontsize: fontsize,
|
131
|
+
fillcolor: fillcolor,
|
132
|
+
href: Wf::Engine.routes.url_helpers.edit_workflow_place_path(self, p))
|
133
|
+
pg_mapping[p] = pg
|
134
|
+
end
|
135
|
+
|
136
|
+
tg_mapping = {}
|
137
|
+
transitions.each do |t|
|
138
|
+
tg = graph.add_nodes(t.name, label: t.name, shape: :box, style: :filled, fillcolor: :lightblue, href: Wf::Engine.routes.url_helpers.edit_workflow_transition_path(self, t))
|
139
|
+
tg_mapping[t] = tg
|
140
|
+
end
|
141
|
+
|
142
|
+
arcs.order("direction desc").each do |arc|
|
143
|
+
label = if arc.guards_count > 0
|
144
|
+
arc.guards.map(&:inspect).join(" & ")
|
145
|
+
else
|
146
|
+
""
|
147
|
+
end
|
148
|
+
if arc.in?
|
149
|
+
graph.add_edges(
|
150
|
+
pg_mapping[arc.place],
|
151
|
+
tg_mapping[arc.transition],
|
152
|
+
label: label,
|
153
|
+
weight: 1,
|
154
|
+
labelfloat: false,
|
155
|
+
labelfontcolor: :red,
|
156
|
+
arrowhead: :vee,
|
157
|
+
href: Wf::Engine.routes.url_helpers.edit_workflow_arc_path(self, arc)
|
158
|
+
)
|
159
|
+
else
|
160
|
+
graph.add_edges(
|
161
|
+
tg_mapping[arc.transition],
|
162
|
+
pg_mapping[arc.place],
|
163
|
+
label: label,
|
164
|
+
weight: 1,
|
165
|
+
labelfloat: false,
|
166
|
+
labelfontcolor: :red,
|
167
|
+
arrowhead: :vee,
|
168
|
+
href: Wf::Engine.routes.url_helpers.edit_workflow_arc_path(self, arc)
|
169
|
+
)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
graph
|
173
|
+
end
|
174
|
+
|
175
|
+
def render_graph(wf_case = nil)
|
176
|
+
graph = to_graph(wf_case)
|
177
|
+
path = Rails.root.join("tmp", "#{id}.svg")
|
178
|
+
graph.output(svg: path)
|
179
|
+
File.read(path)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# == Schema Information
|
4
|
+
#
|
5
|
+
# Table name: wf_workitems
|
6
|
+
#
|
7
|
+
# id :integer not null, primary key
|
8
|
+
# case_id :integer
|
9
|
+
# workflow_id :integer
|
10
|
+
# transition_id :integer
|
11
|
+
# targetable_type :string
|
12
|
+
# targetable_id :string
|
13
|
+
# state :integer default("0")
|
14
|
+
# enabled_at :datetime
|
15
|
+
# started_at :datetime
|
16
|
+
# canceled_at :datetime
|
17
|
+
# finished_at :datetime
|
18
|
+
# overridden_at :datetime
|
19
|
+
# deadline :datetime
|
20
|
+
# created_at :datetime not null
|
21
|
+
# updated_at :datetime not null
|
22
|
+
# trigger_time :datetime
|
23
|
+
# holding_user_id :string
|
24
|
+
#
|
25
|
+
|
26
|
+
module Wf
|
27
|
+
class Workitem < ApplicationRecord
|
28
|
+
belongs_to :workflow
|
29
|
+
belongs_to :transition
|
30
|
+
belongs_to :case
|
31
|
+
belongs_to :targetable, optional: true
|
32
|
+
belongs_to :holding_user, foreign_key: :holding_user_id, class_name: Workflow.user_class.to_s, optional: true
|
33
|
+
has_many :workitem_assignments
|
34
|
+
has_many :parties, through: :workitem_assignments, source: "party"
|
35
|
+
has_many :comments
|
36
|
+
has_many :entries
|
37
|
+
|
38
|
+
enum state: {
|
39
|
+
enabled: 0,
|
40
|
+
started: 1,
|
41
|
+
canceled: 2,
|
42
|
+
finished: 3,
|
43
|
+
overridden: 4
|
44
|
+
}
|
45
|
+
|
46
|
+
def name
|
47
|
+
"Workitem -> #{id}"
|
48
|
+
end
|
49
|
+
|
50
|
+
# TODO: guards exp && target guards
|
51
|
+
def pass_guard?(arc, has_passed = false)
|
52
|
+
if arc.guards_count == 0
|
53
|
+
!has_passed
|
54
|
+
else
|
55
|
+
entry = entries.where(user: holding_user).first
|
56
|
+
arc.guards.all? { |guard| guard.pass?(entry) }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def started_by?(user)
|
61
|
+
enabled? && owned_by?(user)
|
62
|
+
end
|
63
|
+
|
64
|
+
def finished_by?(user)
|
65
|
+
started? && owned_by?(user) && holding_user == user
|
66
|
+
end
|
67
|
+
|
68
|
+
def owned_by?(user)
|
69
|
+
Wf::Party.joins(workitem_assignments: { workitem: %i[transition case] })
|
70
|
+
.where(Wf::Transition.table_name => { trigger_type: Wf::Transition.trigger_types[:user] })
|
71
|
+
.where(Wf::Case.table_name => { state: Wf::Case.states[:active] })
|
72
|
+
.where(Wf::Workitem.table_name => { state: Wf::Workitem.states.values_at(:started, :enabled) })
|
73
|
+
.where(Wf::Workitem.table_name => { id: id }).map do |party|
|
74
|
+
party.partable.users.to_a
|
75
|
+
end.flatten.include?(user)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# == Schema Information
|
4
|
+
#
|
5
|
+
# Table name: wf_workitem_assignments
|
6
|
+
#
|
7
|
+
# id :integer not null, primary key
|
8
|
+
# party_id :integer
|
9
|
+
# workitem_id :integer
|
10
|
+
# created_at :datetime not null
|
11
|
+
# updated_at :datetime not null
|
12
|
+
#
|
13
|
+
|
14
|
+
module Wf
|
15
|
+
class WorkitemAssignment < ApplicationRecord
|
16
|
+
belongs_to :party
|
17
|
+
belongs_to :workitem
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<footer class="footer">
|
2
|
+
<div class="container">
|
3
|
+
<div class="has-text-centered">
|
4
|
+
<p>
|
5
|
+
<strong>
|
6
|
+
<a href="https://github.com/hooopo/petri_flow" target="_blank" rel="nofollow">Petri Flow</a>
|
7
|
+
</strong>
|
8
|
+
crafted by <a href="https://github.com/hooopo" target="_blank" rel="nofollow">Hooopo</a>.
|
9
|
+
The source code is licensed under <a href="http://opensource.org/licenses/mit-license.php">MIT license</a>.
|
10
|
+
</p>
|
11
|
+
</div>
|
12
|
+
</div>
|
13
|
+
</footer>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<nav class="navbar has-shadow" role="navigation" aria-label="main navigation">
|
2
|
+
<div class="container">
|
3
|
+
<div class="navbar-brand">
|
4
|
+
<%= link_to "Petri Flow", root_path, class: "navbar-item" %>
|
5
|
+
</div>
|
6
|
+
|
7
|
+
<div class="navbar-end">
|
8
|
+
<%= link_to current_user&.name, workitems_path %>
|
9
|
+
<%= link_to 'Forms', forms_path, class: "navbar-item #{"is-active" if controller_name == "forms"}" %>
|
10
|
+
<%= link_to 'Workflows', workflows_path, class: "navbar-item #{"is-active" if controller_name.start_with?("workflows")}" %>
|
11
|
+
</div>
|
12
|
+
</div>
|
13
|
+
</nav>
|
@@ -0,0 +1,34 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<title>Petri Flow</title>
|
5
|
+
|
6
|
+
<%= csrf_meta_tags %>
|
7
|
+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
|
8
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
9
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0"/>
|
10
|
+
|
11
|
+
<%= stylesheet_link_tag "wf/application", media: "all", "data-turbolinks-track": "reload" %>
|
12
|
+
<%= javascript_include_tag "wf/application", "data-turbolinks-track": "reload" %>
|
13
|
+
</head>
|
14
|
+
<body>
|
15
|
+
<%= render "layouts/wf/notice" %>
|
16
|
+
<%= render "layouts/wf/alert" %>
|
17
|
+
<%= render "layouts/wf/nav" %>
|
18
|
+
|
19
|
+
<main class="container">
|
20
|
+
<nav aria-label="breadcrumb">
|
21
|
+
<ol class='breadcrumb'>
|
22
|
+
<% breadcrumb_trail do |crumb| %>
|
23
|
+
<li class="breadcrumb-item <%= crumb.current? ? 'active' : '' %>">
|
24
|
+
<%= link_to crumb.name, crumb.url, (crumb.current? ? {'aria-current' => 'page'} : {}) %>
|
25
|
+
</li>
|
26
|
+
<% end %>
|
27
|
+
</ol>
|
28
|
+
</nav>
|
29
|
+
<%= content_for?(:content) ? yield(:content) : yield %>
|
30
|
+
</main>
|
31
|
+
|
32
|
+
<%= render "layouts/wf/footer" %>
|
33
|
+
</body>
|
34
|
+
</html>
|