· 5 years ago · May 06, 2020, 11:54 PM
1class WorkItem < ActiveRecord::Base
2
3 belongs_to :assigned_to, class_name: 'User'
4 belongs_to :created_by, class_name: 'User'
5 belongs_to :completed_by, class_name: 'User'
6 # The `target` is the record that the workItem is associated to currently we would use
7 # ReviewVersion and RecommendationVersion, but we can associate workItems to any
8 # table and they don't need to be versioned, this is defined by target&.duplicate? method
9 belongs_to :target, polymorphic: true
10 # This gives us the ability to rollback work items
11 belongs_to :previous_work_item
12
13 # Describes the type and target of the work item
14 attr_accessor :target_type # Required
15 attr_accessor :target_id # Required
16
17 # Describes the type of work that will be done
18 attr_accessor :work_type
19
20 # Time in days expected to complete the task
21 # The workload is now stored in the db since this shouldn't change historically if
22 # our process becomes more efficient and we change the silo settings
23 attr_accessor :workload
24
25 scope :completed, -> {where("completed_at is not null")}
26 scope :not_completed, -> {where("completed_at is null")}
27 scope :assigned, -> {where("assigned_to_id is not null")}
28
29 # Useful for listing all the available work types as well as showing them ordered
30 WORK_TYPES = [
31 ReviewVersion: [:photo, :test, :write, :edit, :rewrite, :retest, :retestWrite, :retestEdit, :updateTestBench, :untracked],
32 RecommendationVersion: [:write, :edit, :amazon, :amazonEdit, :untracked]
33 ]
34
35 # Useful for determining the next work item to create
36 SEQUENCES = {
37 ReviewVersion: [
38 [:photo, :test, :write, :edit],
39 [:rewrite, :edit],
40 [:retest, :retestWrite, :retestEdit],
41 ],
42 RecommendationVersion: [
43 [:write, :edit, :amazon, :amazonEdit],
44 ],
45 }
46
47 # NOTE: We remove public visible, versioned targets can exists without work items, meaning
48 # they keep track of their public/internal and other relevant versions (like amazon)
49
50 default_scope {order('(completed_at is not null) desc, completed_at asc')}
51
52 def absolute_work_type
53 work_type + target_type
54 end
55
56 def absolute_work_name
57 absolute_work_type.titleize
58 end
59
60 # Public message of why the update was made
61 def message
62 if work_type == :updateTestBench
63 "Converted to <a href=\"#{review_version.test_bench.test_bench_page.url}\">Test Bench #{review_version.test_bench.name}</a>."
64 else
65 public_text
66 end
67 end
68
69 def next_work_type
70 # Finds the next work type and warns if there are multiple options
71 sequences = SEQUENCES.dig(work_type) || []
72 next_type = sequences.map{|s|
73 i = s.indexOf(work_type)
74 i != -1 s[i + 1] : nil
75 }.select(&:present?)
76 if next_type.length < 2
77 return next_type.first
78 else
79 raise 'Multiple work items will be created after this work item is complete'
80 end
81 end
82
83 def complete(user: nil)
84 ActiveRecord::Base.transaction do
85 return false if assigned_to.nil?
86 # We trigger any side effects
87 trigger_side_effects
88 # We complete the work item
89 update_attributes(completed_by: user, completed_at: DateTime.now)
90 # We start a new one if it's in the chain
91 if next_work_type.present?
92 # NOTE: targets should implement a duplicate method to know if we
93 # should duplicate the object or keep reusing the same one.
94 new_target = target&.duplicate? ? target.duplicate : target
95 next_work_item = WorkItem.new(
96 created_by: user,
97 target: new_target,
98 work_type: next_work_type,
99 previous_work_item: self,
100 )
101 next_work_item.workload = next_work_item.calculate_workload
102 next_work_item.save!
103 else
104 end
105 end
106
107 def uncomplete
108 # We trigger any side effects
109 trigger_side_effects(reverse: true)
110 # We complete the work item
111 update_attributes(completed_by: nil, completed_at: nil)
112 end
113
114 def destroy
115 # We destroy this work item and re-instate the old one
116 ActiveRecord::Base.transaction do
117 super
118 previous_work_item.uncomplete if previous_work_item.present?
119 end
120 end
121
122 def trigger_side_effects(reverse: false)
123 if absolute_work_type == "editReviewVersion"
124 # This will set the published_at and the corresponding authors and create any corresponding events
125 reverse ? target.product.product_page.unpublish : target.product.product_page.publish
126 elsif absolute_work_type == "testReviewVersion"
127 reverse ? target.product.product_page.unpublish_early_access : target.product.product_page.publish_early_access
128 elsif absolute_work_type == "editRecommendationVersion"
129 # This will set the published_at and the corresponding authors and create any corresponding events
130 reverse ? target.recommendation.page.unpublish : target.recommendation.page.publish
131 end
132 end
133
134 def complete_preview
135 result = []
136 result << "These changes will appear publicly" if work_type == 'edit'
137 result << "These changes will appear in early access" if work_type == 'test'
138 result << "A new #{next_work_type} WorkItem will be created" if next_work_type.present?
139 return result
140 end
141
142 def calculate_workload
143 # typically called at the time of creation and store in the workload column
144 return 0.25 if absolute_work_type == 'rewriteRecommendationVersion'
145 return self.product.silo.writer_review_days / 2 if absolute_work_type == 'rewriteReviewVersion'
146 return self.product.silo.tester_retest_days if absolute_work_type == 'testBenchUpdateReviewVersion'
147 return self.product.silo.tester_review_days if absolute_work_type == 'testReviewVersion'
148 return self.product.silo.writer_review_days if absolute_work_type == 'writeReviewVersion'
149 return 1 if absolute_work_type == 'writeRecommendationVersion'
150 return 0
151 end
152
153 # ===========================================================================
154 # TARGET PROXY FIELDS
155 # ===========================================================================
156 # fields that normalize some of the targets fields
157
158 def potential; end
159
160 def target_name; end
161
162 def target_admin_url; end
163
164 def silo; end
165
166 def target_admin_url; end
167
168end