mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-10-18 09:47:13 +08:00
Merge pull request #8787 from okriuchykhin/ok_SCI_12213
Improve implementation of team automations observers [SCI-12213]
This commit is contained in:
commit
a95ab59e89
40 changed files with 366 additions and 363 deletions
|
@ -4,7 +4,7 @@ class ApplicationController < ActionController::Base
|
|||
protect_from_forgery with: :exception, prepend: true
|
||||
before_action :authenticate_user!
|
||||
helper_method :current_team
|
||||
before_action :update_current_team, if: :user_signed_in?
|
||||
before_action :set_current_team, if: :user_signed_in?
|
||||
around_action :set_date_format, if: :user_signed_in?
|
||||
around_action :set_time_zone, if: :current_user
|
||||
layout 'main'
|
||||
|
@ -29,11 +29,6 @@ class ApplicationController < ActionController::Base
|
|||
controller_name == 'projects' && action_name == 'index'
|
||||
end
|
||||
|
||||
# Sets current team for all controllers
|
||||
def current_team
|
||||
@current_team ||= current_user.teams.find_by(id: current_user.current_team_id)
|
||||
end
|
||||
|
||||
def to_user_date_format
|
||||
ts = I18n.l(Time.parse(params[:timestamp]),
|
||||
format: params[:ts_format].to_sym)
|
||||
|
@ -87,14 +82,18 @@ class ApplicationController < ActionController::Base
|
|||
|
||||
private
|
||||
|
||||
def update_current_team
|
||||
return if current_team.present? && current_team.id == current_user.current_team_id
|
||||
def current_team
|
||||
@current_team ||= current_user.teams.find_by(id: current_user.current_team_id)
|
||||
end
|
||||
|
||||
def set_current_team
|
||||
if current_user.current_team_id
|
||||
@current_team = current_user.teams.find_by(id: current_user.current_team_id)
|
||||
elsif current_user.teams.any?
|
||||
elsif current_user.teams.first.present?
|
||||
@current_team = current_user.teams.first
|
||||
current_user.update(current_team_id: current_user.teams.first.id)
|
||||
end
|
||||
Current.team = @current_team if @current_team.present?
|
||||
end
|
||||
|
||||
# With this Devise callback user is redirected directly to sign in page instead
|
||||
|
|
|
@ -374,7 +374,7 @@ class MyModulesController < ApplicationController
|
|||
|
||||
def update_state
|
||||
old_status_id = @my_module.my_module_status_id
|
||||
@my_module.my_module_status_created_by = current_user
|
||||
@my_module.status_changed_by = current_user
|
||||
|
||||
if @my_module.update(my_module_status_id: update_status_params[:status_id])
|
||||
log_activity(:change_status_on_task_flow, @my_module, my_module_status_old: old_status_id,
|
||||
|
|
|
@ -133,7 +133,7 @@ class TeamsController < ApplicationController
|
|||
def settings
|
||||
render json: {
|
||||
teamName: @team.name,
|
||||
teamAutomationGroups: Extends::TEAM_AUTOMATION_GROUPS,
|
||||
teamAutomationGroups: Extends::TEAM_AUTOMATIONS_GROUPS,
|
||||
teamSettings: @team.settings,
|
||||
updateUrl: update_settings_team_path(@team)
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ module TeamsHelper
|
|||
if team != current_team && current_user.member_of_team?(team)
|
||||
current_user.current_team_id = team.id
|
||||
current_user.save
|
||||
update_current_team
|
||||
set_current_team
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<td class="py-3 pl-6">{{ i18n.t(`team_automations.sub_group_element.${subGroupElement}`) }}</td>
|
||||
<td class="p-3">
|
||||
<div class="sci-toggle-checkbox-container">
|
||||
<input v-model="teamAutomationSettings[subGroupElement]" type="checkbox" class="sci-toggle-checkbox" @change="setTeamAutomationsSettings"/>
|
||||
<input v-model="teamAutomationSettings[group][subGroup][subGroupElement]" type="checkbox" class="sci-toggle-checkbox" @change="setTeamAutomationsSettings"/>
|
||||
<label class="sci-toggle-checkbox-label"></label>
|
||||
</div>
|
||||
</td>
|
||||
|
@ -41,7 +41,7 @@ import axios from '../../packs/custom_axios.js';
|
|||
|
||||
|
||||
export default {
|
||||
name: 'StorageLocationsContainer',
|
||||
name: 'TeamAutomationsSettingsContainer',
|
||||
components: {},
|
||||
props: {
|
||||
teamUrl: String,
|
||||
|
@ -57,12 +57,21 @@ export default {
|
|||
},
|
||||
computed: {
|
||||
emptySettings() {
|
||||
return Object.entries(this.teamObject.teamAutomationGroups).reduce((settings, [_group, subGroups]) => {
|
||||
Object.values(subGroups).flat().forEach(element => {
|
||||
settings[element] = false;
|
||||
});
|
||||
return settings;
|
||||
}, {});
|
||||
const result = {};
|
||||
|
||||
for (const [group, subGroups] of Object.entries(this.teamObject.teamAutomationGroups)) {
|
||||
result[group] = {};
|
||||
|
||||
for (const [subGroup, settingsArray] of Object.entries(subGroups)) {
|
||||
result[group][subGroup] = {};
|
||||
|
||||
settingsArray.forEach(setting => {
|
||||
result[group][subGroup][setting] = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
|
|
@ -476,8 +476,4 @@ class Asset < ApplicationRecord
|
|||
def reset_file_processing
|
||||
self.file_processing = false
|
||||
end
|
||||
|
||||
def run_observers
|
||||
AutomationObservers::ProtocolContentChangedAutomationObserver.new(step, last_modified_by || created_by).call
|
||||
end
|
||||
end
|
||||
|
|
|
@ -58,10 +58,4 @@ class Checklist < ApplicationRecord
|
|||
new_checklist
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def run_observers
|
||||
AutomationObservers::ProtocolContentChangedAutomationObserver.new(step, last_modified_by || created_by).call
|
||||
end
|
||||
end
|
||||
|
|
|
@ -23,7 +23,6 @@ class ChecklistItem < ApplicationRecord
|
|||
class_name: 'User',
|
||||
optional: true
|
||||
|
||||
after_create :run_observers
|
||||
# conditional touch excluding checked updates
|
||||
after_destroy :touch_checklist
|
||||
after_save :touch_checklist
|
||||
|
@ -78,8 +77,4 @@ class ChecklistItem < ApplicationRecord
|
|||
checklist.touch
|
||||
# rubocop:enable Rails/SkipsModelValidations
|
||||
end
|
||||
|
||||
def run_observers
|
||||
AutomationObservers::ProtocolContentChangedAutomationObserver.new(checklist.step, last_modified_by || created_by).call
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,12 +4,25 @@ module ObservableModel
|
|||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
after_update :run_observers
|
||||
after_create :notify_observers_on_create
|
||||
after_update :notify_observers_on_update
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def run_observers
|
||||
raise NotImplemented
|
||||
def changed_by
|
||||
last_modified_by || created_by
|
||||
end
|
||||
|
||||
def notify_observers_on_create
|
||||
return if Current.team.blank?
|
||||
|
||||
Extends::TEAM_AUTOMATIONS_OBSERVERS_CONFIG[self.class.base_class.name].each { |observer| observer.constantize.on_create(self, changed_by) }
|
||||
end
|
||||
|
||||
def notify_observers_on_update
|
||||
return if Current.team.blank?
|
||||
|
||||
Extends::TEAM_AUTOMATIONS_OBSERVERS_CONFIG[self.class.base_class.name].each { |observer| observer.constantize.on_update(self, changed_by) }
|
||||
end
|
||||
end
|
||||
|
|
5
app/models/current.rb
Normal file
5
app/models/current.rb
Normal file
|
@ -0,0 +1,5 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Current < ActiveSupport::CurrentAttributes
|
||||
attribute :user, :team
|
||||
end
|
|
@ -567,13 +567,6 @@ class Experiment < ApplicationRecord
|
|||
self.due_date_notification_sent = false
|
||||
end
|
||||
|
||||
def run_observers
|
||||
if status_moved_forward? || saved_change_to_project_id || (saved_change_to_archived && !archived)
|
||||
AutomationObservers::ExperimentStatusChangeAutomationObserver.new(self, last_modified_by).call
|
||||
end
|
||||
AutomationObservers::AllExperimentsDoneAutomationObserver.new(project, last_modified_by).call if status_moved_forward?
|
||||
end
|
||||
|
||||
def log_activity(type_of, current_user, my_module)
|
||||
Activities::CreateActivityService
|
||||
.call(activity_type: type_of,
|
||||
|
|
|
@ -47,7 +47,8 @@ class FormFieldValue < ApplicationRecord
|
|||
errors.add(:value, :not_unique_latest)
|
||||
end
|
||||
|
||||
def run_observers
|
||||
AutomationObservers::ProtocolContentChangedAutomationObserver.new(form_response.step, created_by).call
|
||||
# Override for ObservableModel
|
||||
def changed_by
|
||||
created_by
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,7 +17,7 @@ class MyModule < ApplicationRecord
|
|||
include MetadataModel
|
||||
include ObservableModel
|
||||
|
||||
attr_accessor :transition_error_rollback, :my_module_status_created_by
|
||||
attr_accessor :transition_error_rollback, :status_changed_by
|
||||
|
||||
enum state: Extends::TASKS_STATES
|
||||
enum provisioning_status: { done: 0, in_progress: 1, failed: 2 }
|
||||
|
@ -567,7 +567,7 @@ class MyModule < ApplicationRecord
|
|||
|
||||
if status_changing_direction == :forward
|
||||
my_module_status.my_module_status_consequences.each do |consequence|
|
||||
consequence.before_forward_call(self, my_module_status_created_by)
|
||||
consequence.before_forward_call(self, status_changed_by)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -601,12 +601,4 @@ class MyModule < ApplicationRecord
|
|||
self.y = new_pos[:y]
|
||||
end
|
||||
end
|
||||
|
||||
def run_observers
|
||||
if (saved_change_to_my_module_status_id? && my_module_status.previous_status_id == changing_from_my_module_status_id) || saved_change_to_experiment_id ||
|
||||
(saved_change_to_archived && !archived)
|
||||
AutomationObservers::MyModuleStatusChangeAutomationObserver.new(self, last_modified_by).call
|
||||
end
|
||||
AutomationObservers::AllMyModulesDoneAutomationObserver.new(experiment, last_modified_by).call if saved_change_to_my_module_status_id?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,6 +16,7 @@ class Protocol < ApplicationRecord
|
|||
include Assignable
|
||||
include PermissionCheckableModel
|
||||
include TinyMceImages
|
||||
include ObservableModel
|
||||
|
||||
before_create -> { self.skip_user_assignments = true }, if: -> { in_module? }
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ class Result < ApplicationRecord
|
|||
include SearchableModel
|
||||
include SearchableByNameModel
|
||||
include ViewableModel
|
||||
include ObservableModel
|
||||
include Discard::Model
|
||||
|
||||
default_scope -> { kept }
|
||||
|
@ -44,8 +45,6 @@ class Result < ApplicationRecord
|
|||
CleanupUserSettingsJob.perform_later('result_states', id)
|
||||
end
|
||||
|
||||
after_create :run_observers
|
||||
|
||||
def self.search(user,
|
||||
include_archived,
|
||||
query = nil,
|
||||
|
@ -205,7 +204,8 @@ class Result < ApplicationRecord
|
|||
|
||||
private
|
||||
|
||||
def run_observers
|
||||
AutomationObservers::ResultCreateAutomationObserver.new(my_module, user).call
|
||||
# Override for ObservableModel
|
||||
def changed_by
|
||||
last_modified_by || user
|
||||
end
|
||||
end
|
||||
|
|
|
@ -22,7 +22,6 @@ class Step < ApplicationRecord
|
|||
|
||||
before_validation :set_completed_on, if: :completed_changed?
|
||||
before_save :set_last_modified_by
|
||||
after_create :run_observers
|
||||
before_destroy :cascade_before_destroy
|
||||
after_destroy :adjust_positions_after_destroy, unless: -> { skip_position_adjust }
|
||||
|
||||
|
@ -187,17 +186,6 @@ class Step < ApplicationRecord
|
|||
|
||||
private
|
||||
|
||||
def run_observers
|
||||
return unless protocol.in_module?
|
||||
|
||||
if saved_change_to_completed?
|
||||
AutomationObservers::CompletedStepChangeAutomationObserver.new(my_module, last_modified_by).call if completed
|
||||
AutomationObservers::AllCompletedStepsAutomationObserver.new(my_module, last_modified_by).call
|
||||
end
|
||||
|
||||
AutomationObservers::ProtocolContentChangedAutomationObserver.new(self, last_modified_by).call
|
||||
end
|
||||
|
||||
def duplicate_table(new_step, user, table)
|
||||
table.duplicate(new_step, user, table.step_table.step_orderable_element.position)
|
||||
end
|
||||
|
|
|
@ -4,12 +4,9 @@ class StepComment < Comment
|
|||
include ObservableModel
|
||||
|
||||
before_create :fill_unseen_by
|
||||
after_create :run_observers
|
||||
|
||||
belongs_to :step, foreign_key: :associated_id, inverse_of: :step_comments
|
||||
|
||||
validates :step, presence: true
|
||||
|
||||
def commentable
|
||||
step
|
||||
end
|
||||
|
@ -20,7 +17,8 @@ class StepComment < Comment
|
|||
self.unseen_by += step.protocol.my_module.experiment.project.users.where.not(id: user.id).pluck(:id)
|
||||
end
|
||||
|
||||
def run_observers
|
||||
AutomationObservers::ProtocolContentChangedAutomationObserver.new(step, last_modified_by || user).call
|
||||
# Override for ObservableModel
|
||||
def changed_by
|
||||
last_modified_by || user
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,15 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class StepOrderableElement < ApplicationRecord
|
||||
include ObservableModel
|
||||
|
||||
validates :position, uniqueness: { scope: :step }
|
||||
validate :check_step_relations
|
||||
|
||||
after_create :run_observers
|
||||
around_destroy :decrement_following_elements_positions
|
||||
|
||||
belongs_to :step, inverse_of: :step_orderable_elements, touch: true
|
||||
belongs_to :orderable, polymorphic: true, inverse_of: :step_orderable_element
|
||||
|
||||
around_destroy :decrement_following_elements_positions
|
||||
|
||||
private
|
||||
|
||||
def check_step_relations
|
||||
|
@ -28,7 +29,8 @@ class StepOrderableElement < ApplicationRecord
|
|||
end
|
||||
end
|
||||
|
||||
def run_observers
|
||||
AutomationObservers::ProtocolContentChangedAutomationObserver.new(step, step.last_modified_by).call
|
||||
# Override for ObservableModel
|
||||
def changed_by
|
||||
step.last_modified_by
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
class StepText < ApplicationRecord
|
||||
include TinyMceImages
|
||||
include ActionView::Helpers::TextHelper
|
||||
include ObservableModel
|
||||
include ActionView::Helpers::TextHelper
|
||||
|
||||
auto_strip_attributes :name, nullify: false
|
||||
validates :name, length: { maximum: Constants::NAME_MAX_LENGTH }
|
||||
|
@ -39,10 +39,4 @@ class StepText < ApplicationRecord
|
|||
new_step_text
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def run_observers
|
||||
AutomationObservers::ProtocolContentChangedAutomationObserver.new(step, step.last_modified_by).call
|
||||
end
|
||||
end
|
||||
|
|
|
@ -103,8 +103,4 @@ class Table < ApplicationRecord
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
def run_observers
|
||||
AutomationObservers::ProtocolContentChangedAutomationObserver.new(step, step&.last_modified_by).call
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module AutomationObservers
|
||||
class AllCompletedStepsAutomationObserver
|
||||
def initialize(my_module, user)
|
||||
@my_module = my_module
|
||||
@user = user
|
||||
end
|
||||
|
||||
def call
|
||||
return unless @my_module.team.settings.dig('team_automation_settings', 'all_my_module_steps_marked_as_completed')
|
||||
return unless @my_module.my_module_status.previous_status == @my_module.my_module_status_flow.initial_status && @my_module.steps.where(completed: false).none?
|
||||
|
||||
previous_status_id = @my_module.my_module_status.id
|
||||
@my_module.update!(my_module_status: @my_module.my_module_status.next_status)
|
||||
|
||||
Activities::CreateActivityService
|
||||
.call(activity_type: :automation_task_status_changed,
|
||||
owner: @user,
|
||||
team: @my_module.team,
|
||||
project: @my_module.project,
|
||||
subject: @my_module,
|
||||
message_items: {
|
||||
my_module: @my_module.id,
|
||||
my_module_status_old: previous_status_id,
|
||||
my_module_status_new: @my_module.my_module_status.id
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,29 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module AutomationObservers
|
||||
class AllExperimentsDoneAutomationObserver
|
||||
def initialize(project, user)
|
||||
@user = user
|
||||
@project = project
|
||||
end
|
||||
|
||||
def call
|
||||
return unless @project.team.settings.dig('team_automation_settings', 'all_experiments_done')
|
||||
return unless @project.started?
|
||||
return if @project.experiments.active.where.not(id: @project.experiments.active.done).exists?
|
||||
|
||||
@project.update!(status: :done)
|
||||
|
||||
Activities::CreateActivityService
|
||||
.call(activity_type: :automation_project_status_changed,
|
||||
owner: @user,
|
||||
team: @project.team,
|
||||
subject: @project,
|
||||
message_items: {
|
||||
project: @project.id,
|
||||
project_status_old: I18n.t('experiments.table.column.status.in_progress'),
|
||||
project_status_new: I18n.t("experiments.table.column.status.#{@project.status}")
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,28 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module AutomationObservers
|
||||
class AllExperimentsDoneObserver < BaseObserver
|
||||
def self.on_update(experiment, user)
|
||||
return unless Current.team.settings.dig('team_automation_settings', 'projects', 'project_status_done', 'on_all_experiments_done')
|
||||
return unless experiment.status_moved_forward?
|
||||
|
||||
project = experiment.project
|
||||
|
||||
return unless project.started?
|
||||
return if project.experiments.active.where.not(id: project.experiments.active.done).exists?
|
||||
|
||||
project.update!(status: :done, last_modified_by: user)
|
||||
|
||||
Activities::CreateActivityService
|
||||
.call(activity_type: :automation_project_status_changed,
|
||||
owner: user,
|
||||
team: project.team,
|
||||
subject: project,
|
||||
message_items: {
|
||||
project: project.id,
|
||||
project_status_old: I18n.t('experiments.table.column.status.in_progress'),
|
||||
project_status_new: I18n.t("experiments.table.column.status.#{project.status}")
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,30 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module AutomationObservers
|
||||
class AllMyModulesDoneAutomationObserver
|
||||
def initialize(experiment, user)
|
||||
@user = user
|
||||
@experiment = experiment
|
||||
end
|
||||
|
||||
def call
|
||||
return unless @experiment.team.settings.dig('team_automation_settings', 'all_tasks_done')
|
||||
return unless @experiment.started?
|
||||
return unless @experiment.my_modules.active.joins(:my_module_status).where.not(my_module_status: MyModuleStatusFlow.first.final_status).none?
|
||||
|
||||
@experiment.update!(status: :done)
|
||||
|
||||
Activities::CreateActivityService
|
||||
.call(activity_type: :automation_experiment_status_changed,
|
||||
owner: @user,
|
||||
team: @experiment.team,
|
||||
project: @experiment.project,
|
||||
subject: @experiment,
|
||||
message_items: {
|
||||
experiment: @experiment.id,
|
||||
experiment_status_old: I18n.t('experiments.table.column.status.in_progress'),
|
||||
experiment_status_new: I18n.t("experiments.table.column.status.#{@experiment.status}")
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,30 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module AutomationObservers
|
||||
class AllStepsCompletionObserver < BaseObserver
|
||||
def self.on_update(step, user)
|
||||
return unless Current.team.settings.dig('team_automation_settings', 'tasks', 'task_status_completed', 'on_all_steps_completion')
|
||||
return unless step.saved_change_to_completed? && step.completed
|
||||
return unless step.protocol.in_module?
|
||||
|
||||
my_module = step.protocol.my_module
|
||||
|
||||
return unless my_module.my_module_status.previous_status == my_module.my_module_status_flow.initial_status && my_module.steps.where(completed: false).none?
|
||||
|
||||
previous_status_id = my_module.my_module_status.id
|
||||
my_module.update!(my_module_status: my_module.my_module_status.next_status, last_modified_by: user)
|
||||
|
||||
Activities::CreateActivityService
|
||||
.call(activity_type: :automation_task_status_changed,
|
||||
owner: user,
|
||||
team: my_module.team,
|
||||
project: my_module.project,
|
||||
subject: my_module,
|
||||
message_items: {
|
||||
my_module: my_module.id,
|
||||
my_module_status_old: previous_status_id,
|
||||
my_module_status_new: my_module.my_module_status.id
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
27
app/services/automation_observers/all_tasks_done_observer.rb
Normal file
27
app/services/automation_observers/all_tasks_done_observer.rb
Normal file
|
@ -0,0 +1,27 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module AutomationObservers
|
||||
class AllTasksDoneObserver < BaseObserver
|
||||
def self.on_update(my_module, user)
|
||||
return unless Current.team.settings.dig('team_automation_settings', 'experiments', 'experiment_status_done', 'on_all_tasks_done')
|
||||
return unless my_module.saved_change_to_my_module_status_id?
|
||||
return unless my_module.experiment.started?
|
||||
return unless my_module.experiment.my_modules.active.joins(:my_module_status).where.not(my_module_status: MyModuleStatusFlow.first.final_status).none?
|
||||
|
||||
experiment = my_module.experiment
|
||||
experiment.update!(status: :done, last_modified_by: user)
|
||||
|
||||
Activities::CreateActivityService
|
||||
.call(activity_type: :automation_experiment_status_changed,
|
||||
owner: user,
|
||||
team: experiment.team,
|
||||
project: experiment.project,
|
||||
subject: experiment,
|
||||
message_items: {
|
||||
experiment: experiment.id,
|
||||
experiment_status_old: I18n.t('experiments.table.column.status.in_progress'),
|
||||
experiment_status_new: I18n.t("experiments.table.column.status.#{experiment.status}")
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
9
app/services/automation_observers/base_observer.rb
Normal file
9
app/services/automation_observers/base_observer.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module AutomationObservers
|
||||
class BaseObserver
|
||||
def self.on_create(object, user); end
|
||||
|
||||
def self.on_update(object, user); end
|
||||
end
|
||||
end
|
|
@ -1,30 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module AutomationObservers
|
||||
class CompletedStepChangeAutomationObserver
|
||||
def initialize(my_module, user)
|
||||
@my_module = my_module
|
||||
@user = user
|
||||
end
|
||||
|
||||
def call
|
||||
return unless @my_module.team.settings.dig('team_automation_settings', 'step_marked_as_completed')
|
||||
return unless @my_module.my_module_status.initial_status?
|
||||
|
||||
previous_status_id = @my_module.my_module_status.id
|
||||
@my_module.update!(my_module_status: @my_module.my_module_status.next_status)
|
||||
|
||||
Activities::CreateActivityService
|
||||
.call(activity_type: :automation_task_status_changed,
|
||||
owner: @user,
|
||||
team: @my_module.team,
|
||||
project: @my_module.project,
|
||||
subject: @my_module,
|
||||
message_items: {
|
||||
my_module: @my_module.id,
|
||||
my_module_status_old: previous_status_id,
|
||||
my_module_status_new: @my_module.my_module_status.id
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,29 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module AutomationObservers
|
||||
class ExperimentStatusChangeAutomationObserver
|
||||
def initialize(experiment, user)
|
||||
@experiment = experiment
|
||||
@user = user
|
||||
@project = @experiment.project
|
||||
end
|
||||
|
||||
def call
|
||||
return unless @project.team.settings.dig('team_automation_settings', 'experiment_moves_from_not_started_to_in_progress')
|
||||
return unless @project.not_started?
|
||||
return if @experiment.not_started?
|
||||
|
||||
@project.update!(status: :in_progress)
|
||||
Activities::CreateActivityService
|
||||
.call(activity_type: :automation_project_status_changed,
|
||||
owner: @user,
|
||||
team: @project.team,
|
||||
subject: @project,
|
||||
message_items: {
|
||||
project: @project.id,
|
||||
project_status_old: I18n.t('experiments.table.column.status.not_started'),
|
||||
project_status_new: I18n.t("experiments.table.column.status.#{@project.status}")
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,30 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module AutomationObservers
|
||||
class ExperimentStatusChangeObserver < BaseObserver
|
||||
def self.on_update(experiment, user)
|
||||
return unless Current.team.settings.dig('team_automation_settings', 'projects', 'project_status_in_progress', 'on_experiment_in_progress')
|
||||
return unless experiment.status_moved_forward? ||
|
||||
experiment.saved_change_to_project_id ||
|
||||
(experiment.saved_change_to_archived && !experiment.archived)
|
||||
|
||||
project = experiment.project
|
||||
|
||||
return unless project.not_started?
|
||||
return if experiment.not_started?
|
||||
|
||||
project.update!(status: :in_progress, last_modified_by: user)
|
||||
|
||||
Activities::CreateActivityService
|
||||
.call(activity_type: :automation_project_status_changed,
|
||||
owner: user,
|
||||
team: project.team,
|
||||
subject: project,
|
||||
message_items: {
|
||||
project: project.id,
|
||||
project_status_old: I18n.t('experiments.table.column.status.not_started'),
|
||||
project_status_new: I18n.t("experiments.table.column.status.#{project.status}")
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,31 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module AutomationObservers
|
||||
class MyModuleStatusChangeAutomationObserver
|
||||
def initialize(my_module, user)
|
||||
@my_module = my_module
|
||||
@user = user
|
||||
@experiment = @my_module.experiment
|
||||
end
|
||||
|
||||
def call
|
||||
return unless @my_module.team.settings.dig('team_automation_settings', 'task_moves_from_not_started_to_in_progress')
|
||||
return unless @experiment.not_started?
|
||||
return if @my_module.my_module_status.initial_status?
|
||||
|
||||
@experiment.update!(status: :in_progress)
|
||||
|
||||
Activities::CreateActivityService
|
||||
.call(activity_type: :automation_experiment_status_changed,
|
||||
owner: @user,
|
||||
team: @experiment.team,
|
||||
project: @experiment.project,
|
||||
subject: @experiment,
|
||||
message_items: {
|
||||
experiment: @experiment.id,
|
||||
experiment_status_old: I18n.t('experiments.table.column.status.not_started'),
|
||||
experiment_status_new: I18n.t("experiments.table.column.status.#{@experiment.status}")
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,33 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module AutomationObservers
|
||||
class ProtocolContentChangedAutomationObserver
|
||||
def initialize(step, user)
|
||||
@step = step
|
||||
@my_module = step&.my_module
|
||||
@user = user
|
||||
end
|
||||
|
||||
def call
|
||||
return if @step.blank?
|
||||
return unless @step.protocol.in_module?
|
||||
return unless @my_module.team.settings.dig('team_automation_settings', 'protocol_content_added')
|
||||
return unless @my_module.my_module_status.initial_status?
|
||||
|
||||
previous_status_id = @my_module.my_module_status.id
|
||||
@my_module.update!(my_module_status: @my_module.my_module_status.next_status)
|
||||
|
||||
Activities::CreateActivityService
|
||||
.call(activity_type: :automation_task_status_changed,
|
||||
owner: @user,
|
||||
team: @my_module.team,
|
||||
project: @my_module.project,
|
||||
subject: @my_module,
|
||||
message_items: {
|
||||
my_module: @my_module.id,
|
||||
my_module_status_old: previous_status_id,
|
||||
my_module_status_new: @my_module.my_module_status.id
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,30 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module AutomationObservers
|
||||
class ResultCreateAutomationObserver
|
||||
def initialize(my_module, user)
|
||||
@my_module = my_module
|
||||
@user = user
|
||||
end
|
||||
|
||||
def call
|
||||
return @my_module.team.settings.dig('team_automation_settings', 'task_result_added') unless @my_module.team.settings.dig('team_automation_settings', 'task_result_added')
|
||||
return unless @my_module.my_module_status.initial_status?
|
||||
|
||||
previous_status_id = @my_module.my_module_status.id
|
||||
@my_module.update!(my_module_status: @my_module.my_module_status.next_status)
|
||||
|
||||
Activities::CreateActivityService
|
||||
.call(activity_type: :automation_task_status_changed,
|
||||
owner: @user,
|
||||
team: @my_module.team,
|
||||
project: @my_module.project,
|
||||
subject: @my_module,
|
||||
message_items: {
|
||||
my_module: @my_module.id,
|
||||
my_module_status_old: previous_status_id,
|
||||
my_module_status_new: @my_module.my_module_status.id
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
26
app/services/automation_observers/result_create_observer.rb
Normal file
26
app/services/automation_observers/result_create_observer.rb
Normal file
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module AutomationObservers
|
||||
class ResultCreateObserver < BaseObserver
|
||||
def self.on_create(result, user)
|
||||
return unless Current.team.settings.dig('team_automation_settings', 'tasks', 'task_status_in_progress', 'on_added_result')
|
||||
return unless result.my_module.my_module_status.initial_status?
|
||||
|
||||
my_module = result.my_module
|
||||
previous_status_id = my_module.my_module_status.id
|
||||
my_module.update!(my_module_status: my_module.my_module_status.next_status, last_modified_by: user)
|
||||
|
||||
Activities::CreateActivityService
|
||||
.call(activity_type: :automation_task_status_changed,
|
||||
owner: user,
|
||||
team: my_module.team,
|
||||
project: my_module.project,
|
||||
subject: my_module,
|
||||
message_items: {
|
||||
my_module: my_module.id,
|
||||
my_module_status_old: previous_status_id,
|
||||
my_module_status_new: my_module.my_module_status.id
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,27 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module AutomationObservers
|
||||
class StepCompletionObserver < BaseObserver
|
||||
def self.on_update(step, user)
|
||||
return unless Current.team.settings.dig('team_automation_settings', 'tasks', 'task_status_in_progress', 'on_step_completion')
|
||||
return unless step.saved_change_to_completed? && step.completed
|
||||
return unless step.protocol.in_module? && step.protocol.my_module.my_module_status.initial_status?
|
||||
|
||||
my_module = step.protocol.my_module
|
||||
previous_status_id = my_module.my_module_status.id
|
||||
my_module.update!(my_module_status: my_module.my_module_status.next_status, last_modified_by: user)
|
||||
|
||||
Activities::CreateActivityService
|
||||
.call(activity_type: :automation_task_status_changed,
|
||||
owner: user,
|
||||
team: my_module.team,
|
||||
project: my_module.project,
|
||||
subject: my_module,
|
||||
message_items: {
|
||||
my_module: my_module.id,
|
||||
my_module_status_old: previous_status_id,
|
||||
my_module_status_new: my_module.my_module_status.id
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,54 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module AutomationObservers
|
||||
class TaskProtocolContentChangeObserver < BaseObserver
|
||||
def self.on_create(element, user)
|
||||
# Handle creation of an empty protocol alongside with a task
|
||||
return if element.is_a?(Protocol)
|
||||
|
||||
on_update(element, user)
|
||||
end
|
||||
|
||||
def self.on_update(element, user)
|
||||
return unless Current.team.settings.dig('team_automation_settings', 'tasks', 'task_status_in_progress', 'on_protocol_content_change')
|
||||
|
||||
protocol = nil
|
||||
|
||||
case element.class.name
|
||||
when 'Asset', 'Table'
|
||||
return if element.step.blank?
|
||||
|
||||
protocol = element.step.protocol
|
||||
when 'Checklist', 'StepText', 'StepComment', 'StepOrderableElement'
|
||||
protocol = element.step.protocol
|
||||
when 'ChecklistItem'
|
||||
protocol = element.checklist.step.protocol
|
||||
when 'FormFieldValue'
|
||||
protocol = element.form_response.step.protocol
|
||||
when 'Step'
|
||||
protocol = element.protocol
|
||||
when 'Protocol'
|
||||
protocol = element
|
||||
end
|
||||
|
||||
return if protocol.blank?
|
||||
return unless protocol.in_module? && protocol.my_module.my_module_status.initial_status?
|
||||
|
||||
my_module = protocol.my_module
|
||||
previous_status_id = my_module.my_module_status.id
|
||||
my_module.update!(my_module_status: my_module.my_module_status.next_status, last_modified_by: user)
|
||||
|
||||
Activities::CreateActivityService
|
||||
.call(activity_type: :automation_task_status_changed,
|
||||
owner: user,
|
||||
team: my_module.team,
|
||||
project: my_module.project,
|
||||
subject: my_module,
|
||||
message_items: {
|
||||
my_module: my_module.id,
|
||||
my_module_status_old: previous_status_id,
|
||||
my_module_status_new: my_module.my_module_status.id
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module AutomationObservers
|
||||
class TaskStatusChangeObserver < BaseObserver
|
||||
def self.on_update(my_module, user)
|
||||
return unless Current.team.settings.dig('team_automation_settings', 'experiments', 'experiment_status_in_progress', 'on_task_in_progress')
|
||||
return unless my_module.saved_change_to_my_module_status_id? && !my_module.my_module_status.initial_status?
|
||||
return unless my_module.experiment.not_started?
|
||||
|
||||
experiment = my_module.experiment
|
||||
experiment.update!(status: :in_progress, last_modified_by: user)
|
||||
|
||||
Activities::CreateActivityService
|
||||
.call(activity_type: :automation_experiment_status_changed,
|
||||
owner: user,
|
||||
team: experiment.team,
|
||||
project: experiment.project,
|
||||
subject: experiment,
|
||||
message_items: {
|
||||
experiment: experiment.id,
|
||||
experiment_status_old: I18n.t('experiments.table.column.status.not_started'),
|
||||
experiment_status_new: I18n.t("experiments.table.column.status.#{experiment.status}")
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
|
@ -807,35 +807,51 @@ class Extends
|
|||
}
|
||||
}
|
||||
|
||||
TEAM_AUTOMATION_GROUPS = {
|
||||
task_automation: {
|
||||
TEAM_AUTOMATIONS_GROUPS = {
|
||||
tasks: {
|
||||
task_status_in_progress: %I[
|
||||
protocol_content_added
|
||||
step_marked_as_completed
|
||||
task_result_added
|
||||
on_protocol_content_change
|
||||
on_step_completion
|
||||
on_added_result
|
||||
],
|
||||
task_status_completed: %I[
|
||||
all_my_module_steps_marked_as_completed
|
||||
on_all_steps_completion
|
||||
]
|
||||
},
|
||||
experiment_automation: {
|
||||
experiments: {
|
||||
experiment_status_in_progress: %I[
|
||||
task_moves_from_not_started_to_in_progress
|
||||
on_task_in_progress
|
||||
],
|
||||
experiment_status_done: %I[
|
||||
all_tasks_done
|
||||
on_all_tasks_done
|
||||
]
|
||||
},
|
||||
project_automation: {
|
||||
projects: {
|
||||
project_status_in_progress: %I[
|
||||
experiment_moves_from_not_started_to_in_progress
|
||||
on_experiment_in_progress
|
||||
],
|
||||
project_status_done: %I[
|
||||
all_experiments_done
|
||||
on_all_experiments_done
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
TEAM_AUTOMATIONS_OBSERVERS_CONFIG = {
|
||||
'Experiment' => ['AutomationObservers::AllExperimentsDoneObserver', 'AutomationObservers::ExperimentStatusChangeObserver'],
|
||||
'MyModule' => ['AutomationObservers::AllTasksDoneObserver', 'AutomationObservers::TaskStatusChangeObserver'],
|
||||
'Protocol' => ['AutomationObservers::TaskProtocolContentChangeObserver'],
|
||||
'Asset' => ['AutomationObservers::TaskProtocolContentChangeObserver'],
|
||||
'Table' => ['AutomationObservers::TaskProtocolContentChangeObserver'],
|
||||
'StepText' => ['AutomationObservers::TaskProtocolContentChangeObserver'],
|
||||
'ChecklistItem' => ['AutomationObservers::TaskProtocolContentChangeObserver'],
|
||||
'Checklist' => ['AutomationObservers::TaskProtocolContentChangeObserver'],
|
||||
'FormFieldValue' => ['AutomationObservers::TaskProtocolContentChangeObserver'],
|
||||
'StepOrderableElement' => ['AutomationObservers::TaskProtocolContentChangeObserver'],
|
||||
'StepComment' => ['AutomationObservers::TaskProtocolContentChangeObserver'],
|
||||
'Step' => ['AutomationObservers::AllStepsCompletionObserver', 'AutomationObservers::StepCompletionObserver', 'AutomationObservers::TaskProtocolContentChangeObserver'],
|
||||
'Result' => ['AutomationObservers::ResultCreateObserver']
|
||||
}
|
||||
|
||||
DEFAULT_TEAM_SETTINGS = {}
|
||||
|
||||
WHITELISTED_USER_SETTINGS = %w(
|
||||
|
|
|
@ -4330,9 +4330,9 @@ en:
|
|||
team_automations:
|
||||
description: "Automate repetitive work to enhance efficiency. Enabled settings apply to all projects, experiments, and tasks in your current workspace. Automations start when enabled and don't affect past data. Manual changes remain available and will override automated updates."
|
||||
groups:
|
||||
task_automation: 'Task automations'
|
||||
experiment_automation: 'Experiment automations'
|
||||
project_automation: 'Project automations'
|
||||
tasks: 'Task automations'
|
||||
experiments: 'Experiment automations'
|
||||
projects: 'Project automations'
|
||||
sub_groups:
|
||||
task_status_in_progress: 'Automatically update task status to "In progress" when:'
|
||||
task_status_completed: 'Automatically update task status to “Completed” when:'
|
||||
|
@ -4342,14 +4342,14 @@ en:
|
|||
project_status_in_progress: 'Automatically update project status to "In progress" when:'
|
||||
project_status_done: 'Automatically update project status to "Done" when:'
|
||||
sub_group_element:
|
||||
protocol_content_added: 'Protocol content is added (including step comments)'
|
||||
step_marked_as_completed: 'At least one step is marked as completed'
|
||||
task_result_added: 'Task result is added'
|
||||
task_moves_from_not_started_to_in_progress: 'At least one task moves from "Not started" to "In progress" or other status'
|
||||
all_tasks_done: 'All tasks inside reach their final status.'
|
||||
experiment_moves_from_not_started_to_in_progress: 'At least one experiment moves to "In progress" or "Done"'
|
||||
all_experiments_done: 'All experiments inside are marked as "Done"'
|
||||
all_my_module_steps_marked_as_completed: 'All steps are marked as done'
|
||||
on_protocol_content_change: 'Protocol content is added (including step comments)'
|
||||
on_step_completion: 'At least one step is marked as completed'
|
||||
on_added_result: 'Task result is added'
|
||||
on_task_in_progress: 'At least one task moves from "Not started" to "In progress" or other status'
|
||||
on_all_tasks_done: 'All tasks inside reach their final status.'
|
||||
on_experiment_in_progress: 'At least one experiment moves to "In progress" or "Done"'
|
||||
on_all_experiments_done: 'All experiments inside are marked as "Done"'
|
||||
on_all_steps_completion: 'All steps are marked as done'
|
||||
|
||||
notifications:
|
||||
title: "Notifications"
|
||||
|
|
|
@ -26,8 +26,4 @@ describe StepComment, type: :model do
|
|||
describe 'Relations' do
|
||||
it { should belong_to :step }
|
||||
end
|
||||
|
||||
describe 'Validations' do
|
||||
it { should validate_presence_of :step }
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue