2019-05-08 15:24:11 +08:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2017-06-23 21:19:08 +08:00
|
|
|
class Result < ApplicationRecord
|
2019-02-26 18:01:15 +08:00
|
|
|
include ArchivableModel
|
|
|
|
include SearchableModel
|
|
|
|
include SearchableByNameModel
|
2023-08-11 20:48:20 +08:00
|
|
|
include ViewableModel
|
2024-06-26 19:37:00 +08:00
|
|
|
include Discard::Model
|
|
|
|
|
|
|
|
default_scope -> { kept }
|
2016-02-12 23:52:43 +08:00
|
|
|
|
2016-09-21 21:35:23 +08:00
|
|
|
auto_strip_attributes :name, nullify: false
|
2016-10-05 23:45:20 +08:00
|
|
|
validates :name, length: { maximum: Constants::NAME_MAX_LENGTH }
|
2016-02-12 23:52:43 +08:00
|
|
|
|
2024-04-18 21:45:34 +08:00
|
|
|
SEARCHABLE_ATTRIBUTES = ['results.name', 'result_texts.name', 'result_texts.text',
|
2024-05-24 17:24:33 +08:00
|
|
|
'tables.name', 'tables.data_vector', 'comments.message'].freeze
|
2024-04-18 21:45:34 +08:00
|
|
|
|
2023-08-11 20:48:20 +08:00
|
|
|
enum assets_view_mode: { thumbnail: 0, list: 1, inline: 2 }
|
|
|
|
|
2019-06-10 18:19:10 +08:00
|
|
|
belongs_to :user, inverse_of: :results
|
2023-07-26 21:29:13 +08:00
|
|
|
belongs_to :last_modified_by, class_name: 'User', optional: true
|
|
|
|
belongs_to :archived_by, class_name: 'User', optional: true
|
|
|
|
belongs_to :restored_by, class_name: 'User', optional: true
|
2019-05-08 15:24:11 +08:00
|
|
|
belongs_to :my_module, inverse_of: :results
|
2023-09-14 20:22:01 +08:00
|
|
|
delegate :team, to: :my_module
|
2023-07-26 21:29:13 +08:00
|
|
|
has_many :result_orderable_elements, inverse_of: :result, dependent: :destroy
|
|
|
|
has_many :result_assets, inverse_of: :result, dependent: :destroy
|
2024-02-05 20:36:12 +08:00
|
|
|
has_many :assets, through: :result_assets, dependent: :destroy
|
2023-07-26 21:29:13 +08:00
|
|
|
has_many :result_tables, inverse_of: :result, dependent: :destroy
|
2024-02-05 20:36:12 +08:00
|
|
|
has_many :tables, through: :result_tables, dependent: :destroy
|
2023-07-26 21:29:13 +08:00
|
|
|
has_many :result_texts, inverse_of: :result, dependent: :destroy
|
|
|
|
has_many :result_comments, inverse_of: :result, foreign_key: :associated_id, dependent: :destroy
|
2016-02-12 23:52:43 +08:00
|
|
|
has_many :report_elements, inverse_of: :result, dependent: :destroy
|
|
|
|
|
2023-07-26 21:29:13 +08:00
|
|
|
accepts_nested_attributes_for :result_texts
|
|
|
|
accepts_nested_attributes_for :assets
|
|
|
|
accepts_nested_attributes_for :tables
|
2016-02-12 23:52:43 +08:00
|
|
|
|
2023-09-29 19:01:41 +08:00
|
|
|
before_save :ensure_default_name
|
2024-07-30 15:31:21 +08:00
|
|
|
after_discard do
|
|
|
|
CleanupUserSettingsJob.perform_later('result_states', id)
|
|
|
|
end
|
2023-09-29 19:01:41 +08:00
|
|
|
|
2017-05-05 22:41:23 +08:00
|
|
|
def self.search(user,
|
|
|
|
include_archived,
|
|
|
|
query = nil,
|
2024-04-18 21:45:34 +08:00
|
|
|
current_team = nil,
|
2017-05-05 22:41:23 +08:00
|
|
|
options = {})
|
2024-04-18 21:45:34 +08:00
|
|
|
teams = options[:teams] || current_team || user.teams.select(:id)
|
2016-02-12 23:52:43 +08:00
|
|
|
|
2024-04-18 21:45:34 +08:00
|
|
|
new_query = distinct.left_joins(:result_comments, :result_texts, result_tables: :table)
|
|
|
|
.joins(:my_module)
|
|
|
|
.joins("INNER JOIN user_assignments my_module_user_assignments " \
|
|
|
|
"ON my_module_user_assignments.assignable_type = 'MyModule' " \
|
|
|
|
"AND my_module_user_assignments.assignable_id = my_modules.id")
|
|
|
|
.where(my_module_user_assignments: { user_id: user, team_id: teams })
|
2016-07-21 19:11:15 +08:00
|
|
|
|
2024-05-20 19:51:07 +08:00
|
|
|
unless include_archived
|
|
|
|
new_query = new_query.joins(my_module: { experiment: :project })
|
|
|
|
.active
|
|
|
|
.where(my_modules: { archived: false },
|
|
|
|
experiments: { archived: false },
|
|
|
|
projects: { archived: false })
|
|
|
|
end
|
2016-02-12 23:52:43 +08:00
|
|
|
|
2024-05-15 17:59:05 +08:00
|
|
|
new_query.where_attributes_like_boolean(SEARCHABLE_ATTRIBUTES, query, { with_subquery: true, raw_input: new_query })
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.search_subquery(query, raw_input)
|
|
|
|
raw_input.where_attributes_like_boolean(SEARCHABLE_ATTRIBUTES, query)
|
2016-02-12 23:52:43 +08:00
|
|
|
end
|
|
|
|
|
2023-08-23 21:05:31 +08:00
|
|
|
def duplicate(my_module, user, result_name: nil)
|
|
|
|
ActiveRecord::Base.transaction do
|
|
|
|
new_result = my_module.results.new(
|
|
|
|
name: result_name || name,
|
|
|
|
user: user
|
|
|
|
)
|
|
|
|
new_result.save!
|
|
|
|
|
|
|
|
# Copy texts
|
|
|
|
result_texts.each do |result_text|
|
|
|
|
result_text.duplicate(new_result)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Copy assets
|
2023-08-31 21:38:56 +08:00
|
|
|
assets_to_clone = assets.map do |asset|
|
2023-08-23 21:05:31 +08:00
|
|
|
new_asset = asset.dup
|
|
|
|
new_asset.save!
|
|
|
|
new_result.assets << new_asset
|
2023-08-31 21:38:56 +08:00
|
|
|
|
|
|
|
[asset.id, new_asset.id]
|
2023-08-23 21:05:31 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
# Copy tables
|
|
|
|
tables.each do |table|
|
|
|
|
table.duplicate(new_result, user)
|
|
|
|
end
|
|
|
|
|
2023-08-31 21:38:56 +08:00
|
|
|
Result.delay(queue: :assets).deep_clone_assets(assets_to_clone)
|
|
|
|
|
2023-08-23 21:05:31 +08:00
|
|
|
new_result
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-08-31 21:38:56 +08:00
|
|
|
# Deep-clone given array of assets
|
|
|
|
def self.deep_clone_assets(assets_to_clone)
|
|
|
|
ActiveRecord::Base.no_touching do
|
|
|
|
assets_to_clone.each do |src_id, dest_id|
|
|
|
|
src = Asset.find_by(id: src_id)
|
|
|
|
dest = Asset.find_by(id: dest_id)
|
|
|
|
dest.destroy! if src.blank? && dest.present?
|
|
|
|
next unless src.present? && dest.present?
|
|
|
|
|
|
|
|
# Clone file
|
|
|
|
src.duplicate_file(dest)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2023-08-11 20:48:20 +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(my_module: MyModule.viewable_by_user(user, teams))
|
|
|
|
end
|
|
|
|
|
2022-07-07 18:00:35 +08:00
|
|
|
def self.filter_by_teams(teams = [])
|
|
|
|
return self if teams.blank?
|
|
|
|
|
|
|
|
joins(my_module: { experiment: :project }).where(my_module: { experiments: { projects: { team: teams } } })
|
|
|
|
end
|
|
|
|
|
2019-03-28 20:43:50 +08:00
|
|
|
def navigable?
|
|
|
|
!my_module.archived? && my_module.navigable?
|
|
|
|
end
|
|
|
|
|
2016-02-12 23:52:43 +08:00
|
|
|
def space_taken
|
2023-07-26 21:29:13 +08:00
|
|
|
result_assets.joins(asset: { file_attachment: :blob }).sum('active_storage_blobs.byte_size')
|
2016-02-12 23:52:43 +08:00
|
|
|
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 = ResultComment.joins(:result)
|
|
|
|
.where(results: { id: id })
|
|
|
|
.where('comments.id < ?', last_id)
|
|
|
|
.order(created_at: :desc)
|
|
|
|
.limit(per_page)
|
2020-11-30 22:55:20 +08:00
|
|
|
ResultComment.from(comments, :comments).order(created_at: :asc)
|
2016-02-12 23:52:43 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
def is_text
|
2023-07-26 21:29:13 +08:00
|
|
|
raise StandardError, 'Deprecated method, needs to be replaced!'
|
2016-02-12 23:52:43 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
def is_table
|
2023-07-26 21:29:13 +08:00
|
|
|
raise StandardError, 'Deprecated method, needs to be replaced!'
|
2016-02-12 23:52:43 +08:00
|
|
|
end
|
|
|
|
|
|
|
|
def is_asset
|
2023-07-26 21:29:13 +08:00
|
|
|
raise StandardError, 'Deprecated method, needs to be replaced!'
|
2016-02-12 23:52:43 +08:00
|
|
|
end
|
|
|
|
|
2018-02-19 21:47:36 +08:00
|
|
|
def unlocked?(result)
|
2023-07-26 21:29:13 +08:00
|
|
|
result.assets.none?(&:locked?)
|
2019-08-21 17:15:57 +08:00
|
|
|
end
|
2020-11-20 19:29:40 +08:00
|
|
|
|
|
|
|
def comments
|
|
|
|
result_comments
|
|
|
|
end
|
2023-08-21 15:22:56 +08:00
|
|
|
|
|
|
|
def normalize_elements_position
|
|
|
|
result_orderable_elements.order(:position).each_with_index do |element, index|
|
|
|
|
element.update!(position: index) unless element.position == index
|
|
|
|
end
|
|
|
|
end
|
2023-09-29 19:01:41 +08:00
|
|
|
|
|
|
|
def ensure_default_name
|
|
|
|
self.name = name.presence || I18n.t('my_modules.results.default_name')
|
|
|
|
end
|
2016-02-12 23:52:43 +08:00
|
|
|
end
|