2017-06-23 21:19:08 +08:00
|
|
|
class Team < ApplicationRecord
|
2017-01-04 17:54:02 +08:00
|
|
|
include SearchableModel
|
|
|
|
|
2016-02-12 23:52:43 +08:00
|
|
|
# Not really MVC-compliant, but we just use it for logger
|
|
|
|
# output in space_taken related functions
|
|
|
|
include ActionView::Helpers::NumberHelper
|
|
|
|
|
2016-09-21 21:54:12 +08:00
|
|
|
auto_strip_attributes :name, :description, nullify: false
|
2016-02-12 23:52:43 +08:00
|
|
|
validates :name,
|
2016-10-05 23:45:20 +08:00
|
|
|
length: { minimum: Constants::NAME_MIN_LENGTH,
|
|
|
|
maximum: Constants::NAME_MAX_LENGTH }
|
|
|
|
validates :description, length: { maximum: Constants::TEXT_MAX_LENGTH }
|
2016-09-16 17:39:37 +08:00
|
|
|
validates :space_taken, presence: true
|
2016-02-12 23:52:43 +08:00
|
|
|
|
2017-06-28 21:21:32 +08:00
|
|
|
belongs_to :created_by,
|
|
|
|
foreign_key: 'created_by_id',
|
|
|
|
class_name: 'User',
|
|
|
|
optional: true
|
|
|
|
belongs_to :last_modified_by,
|
|
|
|
foreign_key: 'last_modified_by_id',
|
|
|
|
class_name: 'User',
|
|
|
|
optional: true
|
2017-01-24 23:34:21 +08:00
|
|
|
has_many :user_teams, inverse_of: :team, dependent: :destroy
|
|
|
|
has_many :users, through: :user_teams
|
|
|
|
has_many :samples, inverse_of: :team
|
|
|
|
has_many :samples_tables, inverse_of: :team, dependent: :destroy
|
|
|
|
has_many :sample_groups, inverse_of: :team
|
|
|
|
has_many :sample_types, inverse_of: :team
|
|
|
|
has_many :projects, inverse_of: :team
|
|
|
|
has_many :custom_fields, inverse_of: :team
|
|
|
|
has_many :protocols, inverse_of: :team, dependent: :destroy
|
|
|
|
has_many :protocol_keywords, inverse_of: :team, dependent: :destroy
|
2017-04-25 20:24:05 +08:00
|
|
|
has_many :tiny_mce_assets, inverse_of: :team, dependent: :destroy
|
2017-05-18 20:21:00 +08:00
|
|
|
has_many :repositories, dependent: :destroy
|
2018-04-18 22:47:52 +08:00
|
|
|
has_many :reports, inverse_of: :team, dependent: :destroy
|
|
|
|
has_many :datatables_reports,
|
|
|
|
class_name: 'Views::Datatables::DatatablesReport'
|
|
|
|
|
|
|
|
after_commit do
|
2018-04-23 18:26:21 +08:00
|
|
|
Views::Datatables::DatatablesReport.refresh_materialized_view
|
2018-04-18 22:47:52 +08:00
|
|
|
end
|
2016-02-12 23:52:43 +08:00
|
|
|
|
2017-04-13 22:14:12 +08:00
|
|
|
def search_users(query = nil)
|
|
|
|
a_query = "%#{query}%"
|
|
|
|
users.where.not(confirmed_at: nil)
|
2017-04-26 19:32:03 +08:00
|
|
|
.where('full_name ILIKE ? OR email ILIKE ?', a_query, a_query)
|
2017-01-04 17:54:02 +08:00
|
|
|
end
|
|
|
|
|
2016-02-12 23:52:43 +08:00
|
|
|
# Imports samples into db
|
|
|
|
# -1 == sample_name,
|
|
|
|
# -2 == sample_type,
|
|
|
|
# -3 == sample_group
|
|
|
|
# TODO: use constants
|
|
|
|
def import_samples(sheet, mappings, user)
|
2017-07-24 23:27:50 +08:00
|
|
|
errors = false
|
2016-02-12 23:52:43 +08:00
|
|
|
nr_of_added = 0
|
|
|
|
total_nr = 0
|
2017-10-12 20:43:25 +08:00
|
|
|
header_skipped = false
|
2016-02-12 23:52:43 +08:00
|
|
|
|
|
|
|
# First let's query for all custom_fields we're refering to
|
|
|
|
custom_fields = []
|
|
|
|
sname_index = -1
|
|
|
|
stype_index = -1
|
|
|
|
sgroup_index = -1
|
2017-07-24 23:27:50 +08:00
|
|
|
mappings.each.with_index do |(_, v), i|
|
|
|
|
if v == '-1'
|
2016-02-12 23:52:43 +08:00
|
|
|
# Fill blank space, so our indices stay the same
|
|
|
|
custom_fields << nil
|
|
|
|
sname_index = i
|
2017-07-24 23:27:50 +08:00
|
|
|
elsif v == '-2'
|
2016-02-12 23:52:43 +08:00
|
|
|
custom_fields << nil
|
|
|
|
stype_index = i
|
2017-07-24 23:27:50 +08:00
|
|
|
elsif v == '-3'
|
2016-02-12 23:52:43 +08:00
|
|
|
custom_fields << nil
|
2017-07-24 23:27:50 +08:00
|
|
|
sgroup_index = i
|
2016-02-12 23:52:43 +08:00
|
|
|
else
|
|
|
|
cf = CustomField.find_by_id(v)
|
|
|
|
|
|
|
|
# Even if doesn't exist we add nil value in order not to destroy our
|
|
|
|
# indices
|
|
|
|
custom_fields << cf
|
|
|
|
end
|
|
|
|
end
|
2017-10-12 20:43:25 +08:00
|
|
|
|
2017-10-17 20:42:06 +08:00
|
|
|
rows = SpreadsheetParser.spreadsheet_enumerator(sheet)
|
2017-10-12 20:43:25 +08:00
|
|
|
|
2016-02-12 23:52:43 +08:00
|
|
|
# Now we can iterate through sample data and save stuff into db
|
2017-10-12 20:43:25 +08:00
|
|
|
rows.each do |row|
|
|
|
|
# Skip empty rows
|
|
|
|
next if row.empty?
|
|
|
|
unless header_skipped
|
|
|
|
header_skipped = true
|
|
|
|
next
|
|
|
|
end
|
2016-02-12 23:52:43 +08:00
|
|
|
total_nr += 1
|
2018-01-12 00:52:57 +08:00
|
|
|
row = SpreadsheetParser.parse_row(row, sheet)
|
2017-10-12 20:43:25 +08:00
|
|
|
|
|
|
|
sample = Sample.new(name: row[sname_index],
|
2017-07-25 16:56:07 +08:00
|
|
|
team: self,
|
|
|
|
user: user)
|
|
|
|
|
|
|
|
sample.transaction do
|
|
|
|
unless sample.valid?
|
|
|
|
errors = true
|
|
|
|
raise ActiveRecord::Rollback
|
|
|
|
end
|
2016-02-12 23:52:43 +08:00
|
|
|
|
2017-10-12 20:43:25 +08:00
|
|
|
row.each.with_index do |value, index|
|
2017-11-15 23:40:14 +08:00
|
|
|
next unless value.present?
|
2016-02-12 23:52:43 +08:00
|
|
|
if index == stype_index
|
2017-10-13 23:15:51 +08:00
|
|
|
stype = SampleType.where(team: self)
|
|
|
|
.where('name ILIKE ?', value.strip)
|
|
|
|
.take
|
2017-07-24 23:27:50 +08:00
|
|
|
|
2017-07-25 16:56:07 +08:00
|
|
|
unless stype
|
2017-10-13 23:15:51 +08:00
|
|
|
stype = SampleType.new(name: value.strip, team: self)
|
2017-07-25 16:56:07 +08:00
|
|
|
unless stype.save
|
2017-07-24 23:27:50 +08:00
|
|
|
errors = true
|
|
|
|
raise ActiveRecord::Rollback
|
|
|
|
end
|
2016-02-12 23:52:43 +08:00
|
|
|
end
|
2017-07-25 16:56:07 +08:00
|
|
|
sample.sample_type = stype
|
2016-02-12 23:52:43 +08:00
|
|
|
elsif index == sgroup_index
|
2017-10-13 23:15:51 +08:00
|
|
|
sgroup = SampleGroup.where(team: self)
|
|
|
|
.where('name ILIKE ?', value.strip)
|
|
|
|
.take
|
2017-07-24 23:27:50 +08:00
|
|
|
|
2017-07-25 16:56:07 +08:00
|
|
|
unless sgroup
|
2017-10-13 23:15:51 +08:00
|
|
|
sgroup = SampleGroup.new(name: value.strip, team: self)
|
2017-07-25 16:56:07 +08:00
|
|
|
unless sgroup.save
|
2017-07-24 23:27:50 +08:00
|
|
|
errors = true
|
|
|
|
raise ActiveRecord::Rollback
|
2016-02-12 23:52:43 +08:00
|
|
|
end
|
|
|
|
end
|
2017-07-25 16:56:07 +08:00
|
|
|
sample.sample_group = sgroup
|
|
|
|
elsif value && custom_fields[index]
|
|
|
|
# we're working with CustomField
|
|
|
|
scf = SampleCustomField.new(
|
|
|
|
sample: sample,
|
|
|
|
custom_field: custom_fields[index],
|
|
|
|
value: value
|
|
|
|
)
|
|
|
|
unless scf.valid?
|
|
|
|
errors = true
|
|
|
|
raise ActiveRecord::Rollback
|
|
|
|
end
|
|
|
|
sample.sample_custom_fields << scf
|
2016-02-12 23:52:43 +08:00
|
|
|
end
|
|
|
|
end
|
2017-07-25 16:56:07 +08:00
|
|
|
if Sample.import([sample],
|
|
|
|
recursive: true,
|
|
|
|
validate: false).failed_instances.any?
|
|
|
|
errors = true
|
|
|
|
raise ActiveRecord::Rollback
|
|
|
|
end
|
2016-02-12 23:52:43 +08:00
|
|
|
nr_of_added += 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-07-24 23:27:50 +08:00
|
|
|
if errors
|
|
|
|
return { status: :error, nr_of_added: nr_of_added, total_nr: total_nr }
|
2016-02-12 23:52:43 +08:00
|
|
|
else
|
2017-07-24 23:27:50 +08:00
|
|
|
return { status: :ok, nr_of_added: nr_of_added, total_nr: total_nr }
|
2016-02-12 23:52:43 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def to_csv(samples, headers)
|
|
|
|
require "csv"
|
|
|
|
|
|
|
|
# Parse headers (magic numbers should be refactored - see
|
|
|
|
# sample-datatable.js)
|
|
|
|
header_names = []
|
|
|
|
headers.each do |header|
|
|
|
|
if header == "-1"
|
|
|
|
header_names << I18n.t("samples.table.sample_name")
|
|
|
|
elsif header == "-2"
|
|
|
|
header_names << I18n.t("samples.table.sample_type")
|
|
|
|
elsif header == "-3"
|
|
|
|
header_names << I18n.t("samples.table.sample_group")
|
|
|
|
elsif header == "-4"
|
|
|
|
header_names << I18n.t("samples.table.added_by")
|
|
|
|
elsif header == "-5"
|
|
|
|
header_names << I18n.t("samples.table.added_on")
|
|
|
|
else
|
|
|
|
cf = CustomField.find_by_id(header)
|
|
|
|
|
|
|
|
if cf
|
|
|
|
header_names << cf.name
|
|
|
|
else
|
|
|
|
header_names << nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
CSV.generate do |csv|
|
|
|
|
csv << header_names
|
|
|
|
samples.each do |sample|
|
|
|
|
sample_row = []
|
|
|
|
headers.each do |header|
|
|
|
|
if header == "-1"
|
|
|
|
sample_row << sample.name
|
|
|
|
elsif header == "-2"
|
|
|
|
sample_row << (sample.sample_type.nil? ? I18n.t("samples.table.no_type") : sample.sample_type.name)
|
|
|
|
elsif header == "-3"
|
|
|
|
sample_row << (sample.sample_group.nil? ? I18n.t("samples.table.no_group") : sample.sample_group.name)
|
|
|
|
elsif header == "-4"
|
|
|
|
sample_row << sample.user.full_name
|
|
|
|
elsif header == "-5"
|
|
|
|
sample_row << I18n.l(sample.created_at, format: :full)
|
|
|
|
else
|
|
|
|
scf = SampleCustomField.where(
|
|
|
|
custom_field_id: header,
|
|
|
|
sample_id: sample.id
|
|
|
|
).take
|
|
|
|
|
|
|
|
if scf
|
|
|
|
sample_row << scf.value
|
|
|
|
else
|
|
|
|
sample_row << nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
csv << sample_row
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Get all header fields for samples (used in importing for mappings - dropdowns)
|
|
|
|
def get_available_sample_fields
|
|
|
|
fields = {};
|
|
|
|
|
|
|
|
# First and foremost add sample name
|
|
|
|
fields["-1"] = I18n.t("samples.table.sample_name")
|
|
|
|
fields["-2"] = I18n.t("samples.table.sample_type")
|
|
|
|
fields["-3"] = I18n.t("samples.table.sample_group")
|
|
|
|
|
|
|
|
# Add all other custom fields
|
2017-01-24 23:34:21 +08:00
|
|
|
CustomField.where(team_id: id).order(:created_at).each do |cf|
|
2016-02-12 23:52:43 +08:00
|
|
|
fields[cf.id] = cf.name
|
|
|
|
end
|
|
|
|
|
|
|
|
fields
|
|
|
|
end
|
|
|
|
|
2017-01-24 23:34:21 +08:00
|
|
|
# (re)calculate the space taken by this team
|
2016-02-12 23:52:43 +08:00
|
|
|
def calculate_space_taken
|
|
|
|
st = 0
|
|
|
|
projects.includes(
|
2016-08-30 19:06:49 +08:00
|
|
|
experiments: {
|
|
|
|
my_modules: {
|
|
|
|
protocols: { steps: :assets },
|
|
|
|
results: { result_asset: :asset }
|
|
|
|
}
|
|
|
|
}
|
2016-02-12 23:52:43 +08:00
|
|
|
).find_each do |project|
|
2016-07-22 21:36:48 +08:00
|
|
|
project.project_my_modules.find_each do |my_module|
|
2016-07-21 19:11:15 +08:00
|
|
|
my_module.protocol.steps.find_each do |step|
|
2016-02-12 23:52:43 +08:00
|
|
|
step.assets.find_each { |asset| st += asset.estimated_size }
|
2017-04-24 18:28:27 +08:00
|
|
|
step.tiny_mce_assets.find_each { |tiny| st += tiny.estimated_size }
|
2016-02-12 23:52:43 +08:00
|
|
|
end
|
|
|
|
my_module.results.find_each do |result|
|
2017-04-24 18:28:27 +08:00
|
|
|
st += result.asset.estimated_size if result.is_asset
|
|
|
|
if result.is_text
|
|
|
|
tiny_assets = TinyMceAsset.where(result_text: result.result_text)
|
|
|
|
tiny_assets.find_each { |tiny| st += tiny.estimated_size }
|
2016-02-12 23:52:43 +08:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2016-08-09 20:26:08 +08:00
|
|
|
# project.experiments.each |experiment|
|
2017-01-25 19:30:11 +08:00
|
|
|
self.space_taken = [st, Constants::MINIMAL_TEAM_SPACE_TAKEN].max
|
2017-01-24 23:34:21 +08:00
|
|
|
Rails::logger.info "Team #{self.id}: " +
|
2016-02-12 23:52:43 +08:00
|
|
|
"space (re)calculated to: " +
|
|
|
|
"#{self.space_taken}B (#{number_to_human_size(self.space_taken)})"
|
|
|
|
end
|
|
|
|
|
|
|
|
# Take specified amount of bytes
|
|
|
|
def take_space(space)
|
|
|
|
orig_space = self.space_taken
|
|
|
|
self.space_taken += space
|
2017-01-24 23:34:21 +08:00
|
|
|
Rails::logger.info "Team #{self.id}: " +
|
2016-02-12 23:52:43 +08:00
|
|
|
"space taken: " +
|
|
|
|
"#{orig_space}B + #{space}B = " +
|
|
|
|
"#{self.space_taken}B (#{number_to_human_size(self.space_taken)})"
|
|
|
|
end
|
|
|
|
|
|
|
|
# Release specified amount of bytes
|
|
|
|
def release_space(space)
|
|
|
|
orig_space = self.space_taken
|
2016-10-05 23:45:20 +08:00
|
|
|
self.space_taken = [space_taken - space,
|
2017-01-25 19:30:11 +08:00
|
|
|
Constants::MINIMAL_TEAM_SPACE_TAKEN].max
|
2017-01-24 23:34:21 +08:00
|
|
|
Rails::logger.info "Team #{self.id}: " +
|
2016-02-12 23:52:43 +08:00
|
|
|
"space released: " +
|
|
|
|
"#{orig_space}B - #{space}B = " +
|
|
|
|
"#{self.space_taken}B (#{number_to_human_size(self.space_taken)})"
|
|
|
|
end
|
2016-07-21 19:11:15 +08:00
|
|
|
|
|
|
|
def protocol_keywords_list
|
2017-01-24 23:34:21 +08:00
|
|
|
ProtocolKeyword.where(team: self).pluck(:name)
|
2016-07-21 19:11:15 +08:00
|
|
|
end
|
2016-02-12 23:52:43 +08:00
|
|
|
end
|