2017-06-23 21:19:08 +08:00
|
|
|
class Step < ApplicationRecord
|
2016-02-12 23:52:43 +08:00
|
|
|
include SearchableModel
|
2019-02-26 18:01:15 +08:00
|
|
|
include SearchableByNameModel
|
2019-03-11 20:43:50 +08:00
|
|
|
include TinyMceImages
|
2019-06-11 16:08:33 +08:00
|
|
|
include ViewableModel
|
2016-02-12 23:52:43 +08:00
|
|
|
|
2020-11-17 22:33:25 +08:00
|
|
|
enum assets_view_mode: { thumbnail: 0, list: 1, inline: 2 }
|
2020-10-28 18:45:47 +08:00
|
|
|
|
2016-09-21 21:35:23 +08:00
|
|
|
auto_strip_attributes :name, :description, nullify: false
|
2016-10-05 23:45:20 +08:00
|
|
|
validates :name,
|
|
|
|
presence: true,
|
|
|
|
length: { maximum: Constants::NAME_MAX_LENGTH }
|
2016-11-11 18:42:16 +08:00
|
|
|
validates :description, length: { maximum: Constants::RICH_TEXT_MAX_LENGTH }
|
2020-07-28 21:27:05 +08:00
|
|
|
validates :position, presence: true
|
2016-02-12 23:52:43 +08:00
|
|
|
validates :completed, inclusion: { in: [true, false] }
|
2016-07-21 19:11:15 +08:00
|
|
|
validates :user, :protocol, presence: true
|
2017-07-04 20:35:51 +08:00
|
|
|
validates :completed_on, presence: true, if: proc { |s| s.completed? }
|
2020-09-07 16:44:00 +08:00
|
|
|
validates :position, uniqueness: { scope: :protocol }, if: :position_changed?
|
2016-02-12 23:52:43 +08:00
|
|
|
|
2020-07-07 15:34:51 +08:00
|
|
|
before_validation :set_completed_on, if: :completed_changed?
|
|
|
|
before_save :set_last_modified_by
|
2020-07-02 23:03:29 +08:00
|
|
|
before_destroy :cascade_before_destroy
|
2020-07-27 22:14:14 +08:00
|
|
|
after_destroy :adjust_positions_after_destroy
|
2020-07-02 23:03:29 +08:00
|
|
|
|
2019-05-08 19:59:02 +08:00
|
|
|
belongs_to :user, inverse_of: :steps
|
|
|
|
belongs_to :last_modified_by, foreign_key: 'last_modified_by_id', class_name: 'User', optional: true
|
2020-07-27 22:14:14 +08:00
|
|
|
belongs_to :protocol, inverse_of: :steps, touch: true
|
2022-04-19 04:38:49 +08:00
|
|
|
has_many :step_orderable_elements, inverse_of: :step, dependent: :destroy
|
2019-05-08 19:59:02 +08:00
|
|
|
has_many :checklists, inverse_of: :step, dependent: :destroy
|
2017-03-08 20:18:20 +08:00
|
|
|
has_many :step_comments, foreign_key: :associated_id, dependent: :destroy
|
2022-04-19 04:38:49 +08:00
|
|
|
has_many :step_texts, inverse_of: :step, dependent: :destroy
|
2019-05-08 19:59:02 +08:00
|
|
|
has_many :step_assets, inverse_of: :step, dependent: :destroy
|
2016-02-12 23:52:43 +08:00
|
|
|
has_many :assets, through: :step_assets
|
2019-05-08 19:59:02 +08:00
|
|
|
has_many :step_tables, inverse_of: :step, dependent: :destroy
|
2016-02-12 23:52:43 +08:00
|
|
|
has_many :tables, through: :step_tables
|
2019-05-08 19:59:02 +08:00
|
|
|
has_many :report_elements, inverse_of: :step, dependent: :destroy
|
2016-02-12 23:52:43 +08:00
|
|
|
|
|
|
|
accepts_nested_attributes_for :checklists,
|
2017-01-27 23:36:03 +08:00
|
|
|
reject_if: :all_blank,
|
|
|
|
allow_destroy: true
|
2016-02-12 23:52:43 +08:00
|
|
|
accepts_nested_attributes_for :assets,
|
2017-01-27 23:36:03 +08:00
|
|
|
reject_if: :all_blank,
|
|
|
|
allow_destroy: true
|
2016-02-12 23:52:43 +08:00
|
|
|
accepts_nested_attributes_for :tables,
|
2017-01-27 23:36:03 +08:00
|
|
|
reject_if: proc { |attributes|
|
|
|
|
attributes['contents'].blank?
|
|
|
|
},
|
|
|
|
allow_destroy: true
|
2016-02-12 23:52:43 +08:00
|
|
|
|
2022-04-22 19:07:51 +08:00
|
|
|
scope :in_order, -> { order(position: :asc) }
|
|
|
|
scope :desc_order, -> { order(position: :desc) }
|
|
|
|
|
2017-05-05 22:41:23 +08:00
|
|
|
def self.search(user,
|
|
|
|
include_archived,
|
|
|
|
query = nil,
|
|
|
|
page = 1,
|
|
|
|
_current_team = nil,
|
|
|
|
options = {})
|
2021-10-22 17:43:20 +08:00
|
|
|
protocol_ids = Protocol.search(user, include_archived, nil, Constants::SEARCH_NO_LIMIT)
|
|
|
|
.pluck(:id)
|
2016-02-12 23:52:43 +08:00
|
|
|
|
2021-10-22 17:43:20 +08:00
|
|
|
new_query = Step.distinct
|
2022-07-06 20:20:55 +08:00
|
|
|
.left_outer_joins(:step_texts)
|
2021-10-22 17:43:20 +08:00
|
|
|
.where(steps: { protocol_id: protocol_ids })
|
2022-07-06 20:20:55 +08:00
|
|
|
.where_attributes_like(['name', 'step_texts.text'], query, options)
|
2016-02-12 23:52:43 +08:00
|
|
|
|
|
|
|
# Show all results if needed
|
2016-10-05 23:45:20 +08:00
|
|
|
if page == Constants::SEARCH_NO_LIMIT
|
2016-02-12 23:52:43 +08:00
|
|
|
new_query
|
|
|
|
else
|
2021-10-22 17:43:20 +08:00
|
|
|
new_query.limit(Constants::SEARCH_LIMIT).offset((page - 1) * Constants::SEARCH_LIMIT)
|
2016-02-12 23:52:43 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2022-07-07 18:00:35 +08:00
|
|
|
def self.filter_by_teams(teams = [])
|
|
|
|
return self if teams.blank?
|
|
|
|
|
|
|
|
joins(protocol: { my_module: { experiment: :project } })
|
|
|
|
.where(protocol: { my_modules: { experiments: { projects: { team: teams } } } })
|
|
|
|
end
|
|
|
|
|
2019-06-11 16:08:33 +08:00
|
|
|
def default_view_state
|
|
|
|
{ 'assets' => { 'sort' => 'new' } }
|
|
|
|
end
|
|
|
|
|
|
|
|
def validate_view_state(view_state)
|
|
|
|
unless %w(new old atoz ztoa).include?(view_state.state.dig('assets', 'sort'))
|
|
|
|
view_state.errors.add(:state, :wrong_state)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-02-26 18:01:15 +08:00
|
|
|
def self.viewable_by_user(user, teams)
|
|
|
|
where(protocol: Protocol.viewable_by_user(user, teams))
|
|
|
|
end
|
|
|
|
|
2016-10-01 20:51:55 +08:00
|
|
|
def can_destroy?
|
|
|
|
!assets.map(&:locked?).any?
|
|
|
|
end
|
|
|
|
|
2016-07-21 19:11:15 +08:00
|
|
|
def my_module
|
|
|
|
protocol.present? ? protocol.my_module : nil
|
|
|
|
end
|
|
|
|
|
2019-04-03 19:07:30 +08:00
|
|
|
def position_plus_one
|
|
|
|
position + 1
|
|
|
|
end
|
|
|
|
|
2016-10-05 23:45:20 +08:00
|
|
|
def last_comments(last_id = 1, per_page = Constants::COMMENTS_SEARCH_LIMIT)
|
2016-10-13 17:05:11 +08:00
|
|
|
last_id = Constants::INFINITY if last_id <= 1
|
2017-03-08 20:18:20 +08:00
|
|
|
comments = StepComment.joins(:step)
|
|
|
|
.where(steps: { id: id })
|
|
|
|
.where('comments.id < ?', last_id)
|
|
|
|
.order(created_at: :desc)
|
|
|
|
.limit(per_page)
|
2020-11-30 22:55:20 +08:00
|
|
|
StepComment.from(comments, :comments).order(created_at: :asc)
|
2016-02-12 23:52:43 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
def space_taken
|
|
|
|
st = 0
|
|
|
|
assets.each do |asset|
|
|
|
|
st += asset.estimated_size
|
|
|
|
end
|
|
|
|
st
|
|
|
|
end
|
|
|
|
|
2019-05-13 16:54:16 +08:00
|
|
|
def asset_position(asset)
|
2019-10-09 03:40:44 +08:00
|
|
|
assets.order(:updated_at).each_with_index do |step_asset, i|
|
2019-05-13 16:54:16 +08:00
|
|
|
return { count: assets.count, pos: i } if asset.id == step_asset.id
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-09-07 16:44:00 +08:00
|
|
|
def move_up
|
|
|
|
return if position.zero?
|
|
|
|
|
|
|
|
move_in_protocol(:up)
|
|
|
|
end
|
|
|
|
|
|
|
|
def move_down
|
|
|
|
return if position == protocol.steps.count - 1
|
|
|
|
|
|
|
|
move_in_protocol(:down)
|
|
|
|
end
|
|
|
|
|
2020-11-20 19:29:40 +08:00
|
|
|
def comments
|
|
|
|
step_comments
|
|
|
|
end
|
|
|
|
|
2022-06-28 15:56:24 +08:00
|
|
|
def description_step_text
|
|
|
|
step_texts.order(created_at: :asc).first
|
|
|
|
end
|
|
|
|
|
2020-07-02 23:03:29 +08:00
|
|
|
private
|
|
|
|
|
2020-09-07 16:44:00 +08:00
|
|
|
def move_in_protocol(direction)
|
|
|
|
transaction do
|
2020-09-08 17:48:06 +08:00
|
|
|
re_index_following_steps
|
2020-09-07 16:44:00 +08:00
|
|
|
|
|
|
|
case direction
|
|
|
|
when :up
|
|
|
|
new_position = position - 1
|
|
|
|
when :down
|
|
|
|
new_position = position + 1
|
|
|
|
else
|
|
|
|
return
|
|
|
|
end
|
2020-07-28 21:27:05 +08:00
|
|
|
|
2020-09-07 16:44:00 +08:00
|
|
|
step_to_swap = protocol.steps.find_by(position: new_position)
|
|
|
|
position_to_swap = position
|
2020-07-28 21:27:05 +08:00
|
|
|
|
2020-09-07 16:44:00 +08:00
|
|
|
if step_to_swap
|
|
|
|
step_to_swap.update!(position: -1)
|
|
|
|
update!(position: new_position)
|
|
|
|
step_to_swap.update!(position: position_to_swap)
|
|
|
|
else
|
|
|
|
update!(position: new_position)
|
|
|
|
end
|
|
|
|
end
|
2020-07-28 21:27:05 +08:00
|
|
|
end
|
|
|
|
|
2020-07-27 22:14:14 +08:00
|
|
|
def adjust_positions_after_destroy
|
2020-09-08 17:48:06 +08:00
|
|
|
re_index_following_steps
|
|
|
|
protocol.steps.where('position > ?', position).order(:position).each do |step|
|
2020-07-27 22:14:14 +08:00
|
|
|
step.update!(position: step.position - 1)
|
2020-07-02 23:03:29 +08:00
|
|
|
end
|
|
|
|
end
|
2016-02-12 23:52:43 +08:00
|
|
|
|
2020-09-08 17:48:06 +08:00
|
|
|
def re_index_following_steps
|
|
|
|
steps = protocol.steps.where(position: position..).order(:position).where.not(id: id)
|
|
|
|
i = position
|
|
|
|
steps.each do |step|
|
|
|
|
i += 1
|
|
|
|
step.position = i
|
2020-09-07 16:44:00 +08:00
|
|
|
end
|
|
|
|
|
2020-09-08 17:48:06 +08:00
|
|
|
steps.reverse_each do |step|
|
|
|
|
step.save! if step.position_changed?
|
2020-09-07 16:44:00 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-07-02 23:03:29 +08:00
|
|
|
def cascade_before_destroy
|
|
|
|
assets.each(&:destroy)
|
|
|
|
tables.each(&:destroy)
|
2016-02-12 23:52:43 +08:00
|
|
|
end
|
|
|
|
|
2020-07-07 15:34:51 +08:00
|
|
|
def set_completed_on
|
|
|
|
return if completed? && completed_on.present?
|
|
|
|
|
|
|
|
self.completed_on = completed? ? DateTime.now : nil
|
|
|
|
end
|
|
|
|
|
2016-02-12 23:52:43 +08:00
|
|
|
def set_last_modified_by
|
2021-04-10 18:33:32 +08:00
|
|
|
self.last_modified_by_id ||= user_id
|
2016-02-12 23:52:43 +08:00
|
|
|
end
|
|
|
|
end
|