diff --git a/app/controllers/assays_controller.rb b/app/controllers/assays_controller.rb
index 0c88fabba4..007e3f7da9 100644
--- a/app/controllers/assays_controller.rb
+++ b/app/controllers/assays_controller.rb
@@ -70,7 +70,7 @@ def new
@permitted_params = assay_params if params[:assay]
# jump straight to experimental if modelling analysis is disabled
- @assay_class ||= 'experimental' unless Seek::Config.modelling_analysis_enabled
+ @assay_class ||= 'EXP' unless Seek::Config.modelling_analysis_enabled
@assay.assay_class = AssayClass.for_type(@assay_class) unless @assay_class.nil?
@@ -82,7 +82,7 @@ def edit
end
def create
- params[:assay_class_id] ||= AssayClass.for_type('experimental').id
+ params[:assay_class_id] ||= AssayClass.experimental.id
@assay = Assay.new(assay_params)
update_assay_organisms @assay, params
@@ -117,7 +117,7 @@ def fix_assay_linkage
return unless is_single_page_assay?
return unless @assay.has_linked_child_assay?
- previous_assay_linked_st_id = @assay.previous_linked_assay_sample_type&.id
+ previous_assay_linked_st_id = @assay.previous_linked_sample_type&.id
next_assay = Assay.all.detect do |a|
a.sample_type&.sample_attributes&.first&.linked_sample_type_id == @assay.sample_type_id
diff --git a/app/controllers/isa_assays_controller.rb b/app/controllers/isa_assays_controller.rb
index df0643ba43..cc0c8128ed 100644
--- a/app/controllers/isa_assays_controller.rb
+++ b/app/controllers/isa_assays_controller.rb
@@ -6,14 +6,18 @@ class IsaAssaysController < ApplicationController
before_action :find_requested_item, only: %i[edit update]
def new
- @isa_assay = IsaAssay.new
+ if params[:is_assay_stream]
+ @isa_assay = IsaAssay.new({ assay: { assay_class_id: AssayClass.assay_stream.id } })
+ else
+ @isa_assay = IsaAssay.new({ assay: { assay_class_id: AssayClass.experimental.id } })
+ end
end
def create
@isa_assay = IsaAssay.new(isa_assay_params)
update_sharing_policies @isa_assay.assay
@isa_assay.assay.contributor = current_person
- @isa_assay.sample_type.contributor = User.current_user.person
+ @isa_assay.sample_type.contributor = User.current_user.person if isa_assay_params[:sample_type]
if @isa_assay.save
redirect_to single_page_path(id: @isa_assay.assay.projects.first, item_type: 'assay',
item_id: @isa_assay.assay, notice: 'The ISA assay was created successfully!')
@@ -27,7 +31,11 @@ def create
def edit
# let edit the assay if the sample_type is not authorized
- @isa_assay.sample_type = nil unless requested_item_authorized?(@isa_assay.sample_type)
+ if @isa_assay.assay.is_assay_stream?
+ @isa_assay.sample_type = nil
+ else
+ @isa_assay.sample_type = nil unless requested_item_authorized?(@isa_assay.sample_type)
+ end
respond_to do |format|
format.html
@@ -38,9 +46,11 @@ def update
@isa_assay.assay.attributes = isa_assay_params[:assay]
# update the sample_type
- if requested_item_authorized?(@isa_assay.sample_type)
- @isa_assay.sample_type.update(isa_assay_params[:sample_type])
- @isa_assay.sample_type.resolve_inconsistencies
+ unless @isa_assay.assay.is_assay_stream?
+ if requested_item_authorized?(@isa_assay.sample_type)
+ @isa_assay.sample_type.update(isa_assay_params[:sample_type])
+ @isa_assay.sample_type.resolve_inconsistencies
+ end
end
if @isa_assay.save
@@ -87,10 +97,12 @@ def assay_params
{ data_files_attributes: %i[asset_id direction relationship_type_id] },
{ publication_ids: [] },
{ extended_metadata_attributes: determine_extended_metadata_keys(:assay) },
- { discussion_links_attributes: %i[id url label _destroy] }]
+ { discussion_links_attributes: %i[id url label _destroy] }, :assay_stream_id]
end
def sample_type_params(params)
+ return [] unless params[:sample_type]
+
attributes = params[:sample_type][:sample_attributes]
if attributes
params[:sample_type][:sample_attributes_attributes] = []
@@ -128,9 +140,16 @@ def set_up_instance_variable
end
def find_requested_item
- @isa_assay = IsaAssay.new
+ if params[:is_assay_stream]
+ @isa_assay = IsaAssay.new({ assay: { assay_class_id: AssayClass.assay_stream.id } })
+ else
+ @isa_assay = IsaAssay.new({ assay: { assay_class_id: AssayClass.experimental.id } })
+ end
@isa_assay.populate(params[:id])
+ # Should not deal with sample type if assay has assay_class assay stream
+ return if @isa_assay.assay.is_assay_stream?
+
if @isa_assay.sample_type.nil? || !requested_item_authorized?(@isa_assay.assay)
flash[:error] = "You are not authorized to edit this #{t('isa_assay')}"
flash[:error] = 'Resource not found.' if @isa_assay.sample_type.nil?
diff --git a/app/forms/isa_assay.rb b/app/forms/isa_assay.rb
index d5817e0051..a586c75851 100644
--- a/app/forms/isa_assay.rb
+++ b/app/forms/isa_assay.rb
@@ -3,29 +3,31 @@ class IsaAssay
attr_accessor :assay, :sample_type, :input_sample_type_id
- validates_presence_of :assay, :sample_type, :input_sample_type_id
+ validates_presence_of :assay
validate :validate_objects
def initialize(params = {})
@assay = Assay.new(params[:assay] || {})
- @sample_type = SampleType.new((params[:sample_type] || {}).merge({ project_ids: @assay.project_ids }))
- @sample_type.sample_attributes.build(is_title: true, required: true) unless params[:sample_type]
- @assay.sample_type = @sample_type
- @assay.assay_class = AssayClass.for_type('experimental')
+ unless @assay.is_assay_stream?
+ @sample_type = SampleType.new((params[:sample_type] || {}).merge({ project_ids: @assay.project_ids }))
+ @sample_type.sample_attributes.build(is_title: true, required: true) unless params[:sample_type]
+ @assay.sample_type = @sample_type
+ end
+
@input_sample_type_id = params[:input_sample_type_id]
end
def save
if valid?
- if @assay.new_record?
+ if @assay.new_record? && !@assay.is_assay_stream?
# connect the sample type multi link attribute to the last sample type of the assay's study
input_attribute = @sample_type.sample_attributes.detect(&:seek_sample_multi?)
input_attribute.linked_sample_type_id = @input_sample_type_id
title = SampleType.find(@input_sample_type_id).sample_attributes.detect(&:is_title).title
input_attribute.title = "Input (#{title})"
end
+ @sample_type.save unless @assay.is_assay_stream?
@assay.save
- @sample_type.save
else
false
end
@@ -54,6 +56,12 @@ def populate(id)
def validate_objects
@assay.errors.each { |e| errors.add(:base, "[Assay]: #{e.full_message}") } unless @assay.valid?
+ return if @assay.is_assay_stream?
+
+ errors.add(:base, '[Assay]: The assay is missing a sample type.') if @sample_type.nil?
+
+ return unless @sample_type
+
@sample_type.errors.full_messages.each { |e| errors.add(:base, "[Sample type]: #{e}") } unless @sample_type.valid?
unless @sample_type.sample_attributes.any?(&:seek_sample_multi?)
@@ -66,22 +74,22 @@ def validate_objects
unless @sample_type.sample_attributes.select { |a| a.title.include?('Input') && a.isa_tag.nil? }.one?
errors.add(:base,
- "[Sample type]: Should have exactly one attribute with the title 'Input' and no ISA tag".html_safe)
+ "[Sample type]: Should have exactly one attribute with the title 'Input' and no ISA tag".html_safe)
end
if @sample_type.sample_attributes.select { |a| !a.title.include?('Input') && a.isa_tag.nil? }.any?
errors.add(:base,
- "[Sample type]: All attributes should have an ISA Tag except for the 'Input' attribute (hidden)".html_safe)
+ "[Sample type]: All attributes should have an ISA Tag except for the 'Input' attribute (hidden)".html_safe)
end
assay_sample_or_datafile_attributes = @sample_type.sample_attributes.select do |a|
a.isa_tag&.isa_other_material? || a.isa_tag&.isa_data_file?
end
+
unless assay_sample_or_datafile_attributes.one?
errors.add(:base,
- "[Sample type]: Should have exactly one attribute with the 'data_file' or 'other_material' ISA tag selected".html_safe)
+ "[Sample type]: Should have exactly one attribute with the 'data_file' or 'other_material' ISA tag selected".html_safe)
end
-
errors.add(:base, '[Input Assay]: Input Assay is not provided') if @input_sample_type_id.blank?
end
end
diff --git a/app/helpers/images_helper.rb b/app/helpers/images_helper.rb
index a126c4a054..bc23201ea8 100644
--- a/app/helpers/images_helper.rb
+++ b/app/helpers/images_helper.rb
@@ -70,8 +70,8 @@ def append_size_parameter(url, size)
url
end
- def delete_icon(model_item, user, confirm_msg='Are you sure?')
- item_name = text_for_resource model_item
+ def delete_icon(model_item, user, confirm_msg='Are you sure?', alternative_item_name=nil)
+ item_name = alternative_item_name.nil? ? (text_for_resource model_item) : alternative_item_name
if model_item.can_delete?(user)
fullURL = url_for(model_item)
diff --git a/app/models/assay.rb b/app/models/assay.rb
index e2fe2430fa..ab9e5b95e6 100644
--- a/app/models/assay.rb
+++ b/app/models/assay.rb
@@ -25,6 +25,9 @@ class Assay < ApplicationRecord
belongs_to :sample_type
+ has_many :child_assays, class_name: 'Assay', foreign_key: 'assay_stream_id', dependent: :destroy
+ belongs_to :assay_stream, class_name: 'Assay', optional: true
+
belongs_to :assay_class
has_many :assay_organisms, dependent: :destroy, inverse_of: :assay
has_many :organisms, through: :assay_organisms, inverse_of: :assays
@@ -65,19 +68,38 @@ class Assay < ApplicationRecord
enforce_authorization_on_association :study, :view
- def previous_linked_assay_sample_type
- sample_type.sample_attributes.detect { |sa| sa.isa_tag.nil? && sa.title.include?('Input') }&.linked_sample_type
+ def is_assay_stream?
+ assay_class&.is_assay_stream?
+ end
+
+ def previous_linked_sample_type
+ return unless is_isa_json_compliant?
+
+ if is_assay_stream?
+ study.sample_types.second
+ else
+ sample_type.sample_attributes.detect { |sa| sa.isa_tag.nil? && sa.title.include?('Input') }&.linked_sample_type
+ end
end
def has_linked_child_assay?
- sample_type&.linked_sample_attributes&.any?
+ return false unless is_isa_json_compliant?
+
+ if is_assay_stream?
+ child_assays.any?
+ else
+ sample_type&.linked_sample_attributes&.any?
+ end
end
- # Fetches the assay which is linked through linked_sample_attributes (Single Page specific method)
- def linked_assay
- sample_type.linked_sample_attributes
- .select { |lsa| lsa.isa_tag.nil? && lsa.title.include?('Input') }
- .first&.sample_type&.assays&.first
+ def next_linked_child_assay
+ return unless has_linked_child_assay?
+
+ if is_assay_stream?
+ previous_linked_sample_type&.linked_sample_attributes&.detect { |sa| sa.isa_tag.nil? && sa.title.include?('Input') }&.sample_type&.assays&.first
+ else
+ sample_type.linked_sample_attributes.detect { |sa| sa.isa_tag.nil? && sa.title.include?('Input') }&.sample_type&.assays&.first
+ end
end
def default_contributor
@@ -95,7 +117,7 @@ def state_allows_delete?(*args)
end
def is_isa_json_compliant?
- investigation.is_isa_json_compliant? && !sample_type.nil?
+ investigation.is_isa_json_compliant? && (!sample_type.nil? || is_assay_stream?)
end
# returns true if this is a modelling class of assay
diff --git a/app/models/assay_class.rb b/app/models/assay_class.rb
index a7a72dc0fa..5e8940f9ee 100644
--- a/app/models/assay_class.rb
+++ b/app/models/assay_class.rb
@@ -1,31 +1,40 @@
class AssayClass < ApplicationRecord
-
- #this returns an instance of AssayClass according to one of the types "experimental" or "modelling"
- #if there is not a match nil is returned
- def self.for_type type
- keys={"experimental"=>"EXP","modelling"=>"MODEL"}
- return AssayClass.find_by(key: keys[type])
+ # this returns an instance of AssayClass according to one of the constants defined in seek/isa/assay_class.rb
+ # if there is not a match nil is returned
+ def self.for_type(type)
+ AssayClass.find_by(key: type)
end
def self.experimental
- self.for_type('experimental')
+ for_type('EXP')
end
def self.modelling
- self.for_type('modelling')
+ for_type('MODEL')
+
+ end
+
+ def self.assay_stream
+ for_type('STREAM')
end
def is_modelling?
- key == "MODEL"
+ key == 'MODEL'
end
def is_experimental?
key == 'EXP'
end
+ def is_assay_stream?
+ key == 'STREAM'
+ end
+
+ LONG_KEYS = { 'EXP': 'Experimental Assay', 'MODEL': 'Modelling Analysis', 'STREAM': 'Assay Stream' }.freeze
+
# for cases where a longer more descriptive key is useful, but can't rely on the title
# which may have been changed over time
def long_key
- {'EXP'=>'Experimental Assay','MODEL'=>'Modelling Analysis'}[key]
+ LONG_KEYS[key.to_sym]
end
end
diff --git a/app/models/study.rb b/app/models/study.rb
index f7b6371c73..467a550083 100644
--- a/app/models/study.rb
+++ b/app/models/study.rb
@@ -36,6 +36,10 @@ class Study < ApplicationRecord
has_many "related_#{type.pluralize}".to_sym, -> { distinct }, through: :assays, source: type.pluralize.to_sym
end
+ def assay_streams
+ assays.select(&:is_assay_stream?)
+ end
+
def assets
related_data_files + related_sops + related_models + related_publications + related_documents
end
diff --git a/app/views/assays/_buttons.html.erb b/app/views/assays/_buttons.html.erb
index ac5b58e309..72c59241fe 100644
--- a/app/views/assays/_buttons.html.erb
+++ b/app/views/assays/_buttons.html.erb
@@ -1,4 +1,12 @@
-<% assay_word = item.is_modelling? ? t('assays.modelling_analysis') : t('assays.assay') %>
+<% assay_word ||=
+ if item.is_assay_stream?
+ t('assays.assay_stream')
+ elsif Seek::Config.isa_json_compliance_enabled && item.is_isa_json_compliant?
+ t('isa_assay')
+ else
+ t("assays.#{item.assay_class.long_key.delete(' ').underscore}")
+ end
+%>
<%= render :partial => "subscriptions/subscribe", :locals => {:object => item} %>
<% if Seek::Config.project_single_page_enabled %>
@@ -21,10 +29,14 @@
<% if item.can_edit? %>
<% if Seek::Config.isa_json_compliance_enabled && item.is_isa_json_compliant? %>
- <% valid_study = item&.study&.sample_types.present? %>
- <% valid_assay = item&.sample_type.present? %>
+ <% valid_study = item&.study&.is_isa_json_compliant? %>
+ <% valid_assay = item&.is_isa_json_compliant? %>
<% if valid_study && valid_assay %>
- <%= button_link_to("Design the next #{t('assay')}", 'new', new_isa_assay_path(source_assay_id: item.id, study_id: item.study.id, single_page: params[:single_page])) %>
+ <% if item&.is_assay_stream? %>
+ <%= button_link_to("Design #{t('assay')}", 'new', new_isa_assay_path(source_assay_id: item.id, study_id: item.study.id, single_page: params[:single_page], assay_stream_id: item.id)) %>
+ <% else %>
+ <%= button_link_to("Design the next #{t('assay')}", 'new', new_isa_assay_path(source_assay_id: item.id, study_id: item.study.id, single_page: params[:single_page], assay_stream_id: item.assay_stream_id)) %>
+ <% end %>
<% end %>
<% else %>
<%= add_new_item_to_dropdown(item) %>
@@ -38,7 +50,11 @@
<%= item_actions_dropdown do %>
<% if item.can_edit? %>
<% if Seek::Config.isa_json_compliance_enabled && item.is_isa_json_compliant? %>
-
<%= form_for @isa_assay, url: { controller: "isa_assays", action: "update", id: params[:id] }, method: :put do |f| %>
diff --git a/app/views/isa_assays/new.html.erb b/app/views/isa_assays/new.html.erb
index d2a46bab7b..07da9cf1e0 100644
--- a/app/views/isa_assays/new.html.erb
+++ b/app/views/isa_assays/new.html.erb
@@ -1,5 +1,12 @@
-
New <%=t("isa_assay")%>
+
+ <%= sample_controlled_vocab_model_dialog('cv-modal') %>
+
+ <% if params[:is_assay_stream] %>
+ New <%=t("assays.assay_stream")%>
+ <% else %>
+ New <%=t("isa_assay")%>
+ <% end %>
<%= render partial: "templates/template_modal" -%>
diff --git a/app/views/isa_studies/_sample_types_form.html.erb b/app/views/isa_studies/_sample_types_form.html.erb
index 1e78428601..b2c7605af6 100644
--- a/app/views/isa_studies/_sample_types_form.html.erb
+++ b/app/views/isa_studies/_sample_types_form.html.erb
@@ -5,9 +5,6 @@
<% main_field_name = id_suffix[1..-1] %>
<% isa_element ||= "study" %>
-
- <%= sample_controlled_vocab_model_dialog('cv-modal') %>
-
<%= f.fields_for main_field_name, sample_type do |field| %>
<% unless action == :edit %>
diff --git a/app/views/isa_studies/new.html.erb b/app/views/isa_studies/new.html.erb
index 38f08dc616..021e3e17f4 100644
--- a/app/views/isa_studies/new.html.erb
+++ b/app/views/isa_studies/new.html.erb
@@ -1,6 +1,10 @@
<% if Investigation.authorized_for('view').none? %>
<%= button_link_to("New #{t('investigation')}", 'arrow_right', new_investigation_path) -%>
<% else %>
+
+ <%= sample_controlled_vocab_model_dialog('cv-modal') %>
+
+
New <%=t('isa_study')%>
<%= render :partial => "templates/template_modal" -%>
diff --git a/app/views/studies/_buttons.html.erb b/app/views/studies/_buttons.html.erb
index b27edc214f..a308561224 100644
--- a/app/views/studies/_buttons.html.erb
+++ b/app/views/studies/_buttons.html.erb
@@ -21,7 +21,7 @@
<% if item.can_edit? -%>
<% if Seek::Config.isa_json_compliance_enabled && item.is_isa_json_compliant? %>
<% if item&.sample_types.present? %>
- <%= button_link_to("Design #{t('assay')}", 'new', new_isa_assay_path(study_id: item.id, single_page: params[:single_page], is_source: true)) %>
+ <%= button_link_to("Design #{t('assays.assay_stream')}", 'new', new_isa_assay_path(study_id: item.id, single_page: params[:single_page], is_assay_stream: true)) %>
<% end -%>
<% else -%>
<%= add_new_item_to_dropdown(item) %>
@@ -38,7 +38,12 @@
<% end %>
<% if item.can_manage? -%>
-
<%= image_tag_for_key('manage', manage_study_path(item), "Manage #{t('study')}", nil, "Manage #{t('study')}") -%>
+ <% if Seek::Config.isa_json_compliance_enabled && item.is_isa_json_compliant? %>
+
<%= image_tag_for_key('manage', manage_study_path(item), "Manage #{t('study')}", nil, "Manage #{t('isa_study')}") -%>
+ <% else %>
+
<%= image_tag_for_key('manage', manage_study_path(item), "Manage #{t('study')}", nil, "Manage #{t('study')}") -%>
+ <% end %>
+
<%= render :partial => 'snapshots/new_snapshot_link', :locals => { :item => item } %>
<% end -%>
diff --git a/config/default_data/assay_classes.yml b/config/default_data/assay_classes.yml
index 94a08a2a99..8b01bc6ad3 100644
--- a/config/default_data/assay_classes.yml
+++ b/config/default_data/assay_classes.yml
@@ -6,4 +6,9 @@ experimental_assay_class:
modelling_assay_class:
id: 2
title: <%= I18n.t('assays.modelling_analysis') %>
- key: MODEL
\ No newline at end of file
+ key: MODEL
+
+assay_stream_class:
+ id: 3
+ title: <%= I18n.t('assays.assay_stream') %>
+ key: STREAM
diff --git a/config/locales/en.yml b/config/locales/en.yml
index ced43e1a21..8880ff79e8 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -16,6 +16,7 @@ en:
assay: "Assay"
experimental_assay: "Experimental assay"
modelling_analysis: "Modelling analysis"
+ assay_stream: "Assay Stream"
isa_study: "ISA Study"
study_design: "Study Design"
diff --git a/config/routes.rb b/config/routes.rb
index e632e6f4f0..49bdcba035 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -744,7 +744,7 @@
post :template_attributes
end
collection do
-post :filter_isa_tags_by_level
+ post :filter_isa_tags_by_level
get :task_status
get :default_templates
post :populate_template
diff --git a/db/migrate/20240112141513_add_assay_stream_id_to_assay.rb b/db/migrate/20240112141513_add_assay_stream_id_to_assay.rb
new file mode 100644
index 0000000000..6220df0185
--- /dev/null
+++ b/db/migrate/20240112141513_add_assay_stream_id_to_assay.rb
@@ -0,0 +1,6 @@
+class AddAssayStreamIdToAssay < ActiveRecord::Migration[6.1]
+ def change
+ add_column :assays, :assay_stream_id, :integer
+ add_index :assays, :assay_stream_id
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 22e12632eb..2f51e08778 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2023_12_18_133053) do
+ActiveRecord::Schema.define(version: 2024_01_12_141513) do
create_table "activity_logs", id: :integer, force: :cascade do |t|
t.string "action"
@@ -189,6 +189,8 @@
t.string "deleted_contributor"
t.integer "sample_type_id"
t.integer "position"
+ t.integer "assay_stream_id"
+ t.index ["assay_stream_id"], name: "index_assays_on_assay_stream_id"
t.index ["sample_type_id"], name: "index_assays_on_sample_type_id"
end
diff --git a/lib/isa_exporter.rb b/lib/isa_exporter.rb
index 108144752a..5d5a3ff288 100644
--- a/lib/isa_exporter.rb
+++ b/lib/isa_exporter.rb
@@ -117,7 +117,7 @@ def convert_study(study)
# raise "The Study with the title '#{study.title}' does not have any SOP" if study.sops.blank?
protocols << convert_protocol(study.sops, study.id, with_tag_protocol_study, with_tag_parameter_value_study)
- study.assays.each do |a|
+ study.assay_streams.map(&:child_assays).flatten.each do |a|
# There should be only one attribute with isa_tag == protocol
protocol_attribute = a.sample_type.sample_attributes.detect { |sa| sa.isa_tag&.isa_protocol? }
with_tag_parameter_value = a.sample_type.sample_attributes.select { |sa| sa.isa_tag&.isa_parameter_value? }
@@ -134,20 +134,7 @@ def convert_study(study)
raise "All assays in study `#{study.title}` should be ISA-JSON compliant."
end
- assay_streams = study.assays.map { |assay| [assay] if assay&.position&.zero? }
- .compact
- .map do |assay_stream|
- last_assay = assay_stream.first
- until last_assay.linked_assay.nil?
- linked_assay = last_assay.linked_assay
- assay_stream.push(linked_assay)
- last_assay = linked_assay
- end
- assay_stream
- end
-
- isa_study[:assays] = assay_streams.map { |assay_stream| convert_assays(assay_stream) }.compact
-
+ isa_study[:assays] = study.assay_streams.map { |assay_stream| convert_assays(assay_stream) }.compact
isa_study[:factors] = []
isa_study[:unitCategories] = []
@@ -163,28 +150,25 @@ def convert_annotation(term_uri)
isa_annotation
end
- def convert_assay_comments(assays)
+ def convert_assay_comments(assay_stream)
assay_comments = []
- assay_streams = assays.select { |a| a.position.zero? }
- assay_stream_id = assays.pluck(:id).join('_')
-
- linked_assays = assays.map { |assay| { 'id': assay.id, 'title': assay.title } }.to_json
-
- assay_streams.map do |assay|
- study_id = assay.study_id
- next if assay.extended_metadata.nil?
-
- json = JSON.parse(assay.extended_metadata&.json_metadata)
- cm_attributes = assay.extended_metadata.extended_metadata_attributes
- cm_id = assay.extended_metadata&.id
- json.map do |key, val|
- cma_id = cm_attributes.detect { |cma| cma.title == key }&.id
- assay_comments.push({
- '@id': "#assay_comment/#{[study_id, assay_stream_id, cm_id, cma_id].join('_')}",
- 'name': key,
- 'value': val
- })
- end
+ assay_stream_id = assay_stream&.id
+
+ linked_assays = assay_stream.child_assays.map { |assay| { 'id': assay.id, 'title': assay.title } }.to_json
+
+ study_id = assay_stream.study_id
+ return [] if assay_stream.extended_metadata.nil?
+
+ json = JSON.parse(assay_stream.extended_metadata&.json_metadata)
+ cm_attributes = assay_stream.extended_metadata.extended_metadata_attributes
+ cm_id = assay_stream.extended_metadata&.id
+ json.map do |key, val|
+ cma_id = cm_attributes.detect { |cma| cma.title == key }&.id
+ assay_comments.push({
+ '@id': "#assay_comment/#{[study_id, assay_stream_id, cm_id, cma_id].join('_')}",
+ 'name': key,
+ 'value': val
+ })
end
assay_comments.push({
@@ -195,35 +179,43 @@ def convert_assay_comments(assays)
assay_comments.compact
end
- def convert_assays(assays)
- return unless assays.all? { |a| a.can_view?(@current_user) }
+ def convert_assays(assay_stream)
+ child_assays = assay_stream.child_assays
+ return unless assay_stream.can_view?(@current_user)
+ return unless child_assays.all? { |a| a.can_view?(@current_user) }
- all_sample_types = assays.map(&:sample_type)
- first_assay = assays.detect { |s| s.position.zero? }
- raise 'No assay could be found!' unless first_assay
+ child_assays.map do |ca|
+ unless ca.sample_type.present?
+ raise "No Sample type was found in Assay '#{ca.id} - #{ca.title}'," \
+ " part of Assay Stream '#{assay_stream.id - assay_stream.title}'"
+ end
+ end
- stream_name = "assays_#{assays.pluck(:id).join('_')}"
- assay_comments = convert_assay_comments(assays)
+ all_sample_types = child_assays.map(&:sample_type).compact
+
+ # stream_name = "assays_#{child_assays.pluck(:id).join('_')}"
+ stream_name = "#{ assay_stream.title }_#{assay_stream.id}_#{child_assays.pluck(:id).join('_')}"
+ assay_comments = convert_assay_comments(assay_stream)
# Retrieve assay_stream if
- stream_name_comment = assay_comments.detect { |ac| ac[:name] == 'assay_stream' }
- stream_name = stream_name_comment[:value] unless stream_name_comment.nil?
+ # stream_name_comment = assay_comments.detect { |ac| ac[:name] == 'assay_stream' }
+ # stream_name = stream_name_comment[:value] unless stream_name_comment.nil?
isa_assay = {}
- isa_assay['@id'] = "#assay/#{assays.pluck(:id).join('_')}"
+ isa_assay['@id'] = "#assay/#{child_assays.pluck(:id).join('_')}"
isa_assay[:filename] = "a_#{stream_name.downcase.tr(" ", "_")}.txt"
isa_assay[:measurementType] = { annotationValue: '', termSource: '', termAccession: '' }
isa_assay[:technologyType] = { annotationValue: '', termSource: '', termAccession: '' }
isa_assay[:comments] = assay_comments
isa_assay[:technologyPlatform] = ''
- isa_assay[:characteristicCategories] = convert_characteristic_categories(nil, assays)
+ isa_assay[:characteristicCategories] = convert_characteristic_categories(nil, child_assays)
isa_assay[:materials] = {
# Here, the first assay's samples will be enough
- samples: assay_samples(first_assay), # the samples from study level that are referenced in this assay's samples,
+ samples: assay_samples(child_assays.first), # the samples from study level that are referenced in this assay's samples,
otherMaterials: convert_other_materials(all_sample_types)
}
isa_assay[:processSequence] =
- assays.map { |a| convert_process_sequence(a.sample_type, a.sops.map(&:id).join("_"), a.id) }.flatten
+ child_assays.map { |a| convert_process_sequence(a.sample_type, a.sops.map(&:id).join("_"), a.id) }.flatten
isa_assay[:dataFiles] = convert_data_files(all_sample_types)
isa_assay[:unitCategories] = []
isa_assay
@@ -278,7 +270,14 @@ def convert_publication(publication)
def convert_ontologies
source_ontologies = []
- sample_types = @investigation.studies.map(&:sample_types) + @investigation.assays.map(&:sample_type)
+ sample_types = @investigation.studies.map(&:sample_types) + @investigation.assays
+ .select(&:is_assay_stream?)
+ .map(&:child_assays)
+ .compact
+ .flatten
+ .map(&:sample_type)
+ .compact
+
sample_types.flatten.each do |sa|
sa.sample_attributes.each do |atr|
source_ontologies << atr.sample_controlled_vocab.source_ontology if atr.ontology_based?
@@ -752,7 +751,7 @@ def random_string(len)
end
def get_derived_from_type(sample_type)
- raise 'There is no sample!' if sample_type.samples.length == 0
+ raise "There are no samples in '#{sample_type.title}'!" if sample_type.samples.blank?
prev_sample_type = sample_type.samples[0]&.linked_samples[0]&.sample_type
return nil if prev_sample_type.blank?
diff --git a/lib/seek/ontologies/synchronize.rb b/lib/seek/ontologies/synchronize.rb
index e15f0119e8..cfce103be3 100644
--- a/lib/seek/ontologies/synchronize.rb
+++ b/lib/seek/ontologies/synchronize.rb
@@ -99,7 +99,7 @@ def assay_changes_class?(assays, ontology_uri)
def determine_assay_class_from_uri(uri)
ontology_class = Seek::Ontologies::AssayTypeReader.instance.class_for_uri(uri)
- ontology_class.nil? ? AssayClass.for_type('modelling') : AssayClass.for_type('experimental')
+ ontology_class.nil? ? AssayClass.modelling : AssayClass.experimental
end
def get_suggested_types_found_in_ontology(type)
diff --git a/lib/seek/openbis/seek_util.rb b/lib/seek/openbis/seek_util.rb
index 6476676d54..a99b15949e 100644
--- a/lib/seek/openbis/seek_util.rb
+++ b/lib/seek/openbis/seek_util.rb
@@ -27,7 +27,7 @@ def createObisAssay(assay_params, creator, obis_asset)
zample = obis_asset.content
openbis_endpoint = obis_asset.seek_service
- assay_params[:assay_class_id] ||= AssayClass.for_type('experimental').id
+ assay_params[:assay_class_id] ||= AssayClass.experimental.id
assay_params[:title] ||= extract_title(zample) ## "OpenBIS #{zample.perm_id}"
assay = Assay.new(assay_params)
@@ -88,7 +88,7 @@ def fake_file_assay(study)
assay = study.assays.where(title: FAKE_FILE_ASSAY_NAME).first
return assay if assay
- assay_params = {assay_class_id: AssayClass.for_type('experimental').id,
+ assay_params = {assay_class_id: AssayClass.experimental.id,
title: FAKE_FILE_ASSAY_NAME,
description: 'Automatically generated assay to host openbis files that are linked to
the original OpenBIS experiment. Its content and linked data files will be updated by the system
@@ -446,7 +446,7 @@ def study_types(openbis_endpoint)
Seek::Openbis::EntityType.ExperimentType(openbis_endpoint).find_by_codes(study_codes)
end
-
+
end
end
end
diff --git a/lib/seek/projects/population.rb b/lib/seek/projects/population.rb
index 2ab008c1e4..faeb03241b 100644
--- a/lib/seek/projects/population.rb
+++ b/lib/seek/projects/population.rb
@@ -21,7 +21,7 @@ def populate_from_spreadsheet_impl
r = sheet.rows[1]
if r.cell(1).value.blank?
- flash[:error]= "Unable to find header cells in #{datafile.title}"
+ flash[:error]= "Unable to find header cells in #{datafile.title}"
return
end
@@ -78,7 +78,7 @@ def populate_from_spreadsheet_impl
assay_position = 1
investigation.save!
end
-
+
if r.cell(study_index)&.value.present?
if investigation.blank?
flash[:error]= "Study specified without Investigation in #{datafile.title} at row #{r.index}"
@@ -111,7 +111,7 @@ def populate_from_spreadsheet_impl
set_description(assay, r, description_index)
assay.position = assay_position
assay_position += 1
- assay.assay_class = AssayClass.for_type('experimental')
+ assay.assay_class = AssayClass.experimental
set_assignees(assay, r, assignee_indices)
@@ -134,7 +134,7 @@ def set_description(object, r, description_index)
end
object.description = description
end
-
+
def set_assignees(assay, r, assignee_indices)
assignees = []
assignee_indices.each do |x|
@@ -154,7 +154,7 @@ def set_assignees(assay, r, assignee_indices)
end
assay.creators = known_creators
assay.other_creators = other_creators.join(';')
- end
+ end
def set_protocol(assay, r, protocol_index)
protocol_string = r.cell(protocol_index)&.value&.to_s.strip
diff --git a/lib/tasks/seek_upgrades.rake b/lib/tasks/seek_upgrades.rake
index fb779e184a..e9b1ae3c15 100644
--- a/lib/tasks/seek_upgrades.rake
+++ b/lib/tasks/seek_upgrades.rake
@@ -15,7 +15,9 @@ namespace :seek do
rename_registered_sample_multiple_attribute_type
remove_ontology_attribute_type
db:seed:007_sample_attribute_types
+ db:seed:001_create_controlled_vocabs
recognise_isa_json_compliant_items
+ implement_assay_streams_for_isa_assays
]
# these are the tasks that are executes for each upgrade as standard, and rarely change
@@ -176,6 +178,48 @@ namespace :seek do
puts "...Updated #{investigations_updated.to_s} investigations"
end
+ task(implement_assay_streams_for_isa_assays: [:environment]) do
+ puts '... Organising isa json compliant assays in assay streams'
+ assay_streams_created = 0
+ disable_authorization_checks do
+ # find assays linked to a study through their sample_types
+ # Should be isa json compliant
+ # Shouldn't already have an assay stream (don't update assays that have been updated already)
+ # Previous ST should be second ST of study
+ first_assays_in_stream = Assay.joins(:sample_type, study: :investigation)
+ .where(assay_stream_id: nil, investigation: { is_isa_json_compliant: true })
+ .select { |a| a.previous_linked_sample_type == a.study.sample_types.second }
+
+ first_assays_in_stream.map do |fas|
+ stream_name = "Assay Stream - #{UUID.generate}"
+ assay_stream = Assay.create(title: stream_name,
+ study_id: fas.study_id,
+ assay_class_id: AssayClass.assay_stream.id,
+ contributor: fas.contributor,
+ position: 0)
+
+ # Transfer extended metadata from first assay to newly created assay stream
+ unless fas.extended_metadata.nil?
+ em = ExtendedMetadata.find_by(item_id: fas.id)
+ em.update_column(:item_id, assay_stream.id)
+ end
+
+ assay_position = 1
+ current_assay = fas
+ while current_assay
+ current_assay.update_column(:position, assay_position)
+ current_assay.update_column(:assay_stream_id, assay_stream.id)
+
+ assay_position += 1
+ current_assay = current_assay.next_linked_child_assay
+ end
+ assay_streams_created += 1
+ end
+ end
+
+ puts "...Created #{assay_streams_created} new assay streams"
+ end
+
private
##
diff --git a/lib/treeview_builder.rb b/lib/treeview_builder.rb
index d64e6f4217..6f89ddf2d3 100644
--- a/lib/treeview_builder.rb
+++ b/lib/treeview_builder.rb
@@ -14,9 +14,22 @@ def build_tree_data
investigation_items = []
@project.investigations.map do |investigation|
- investigation.studies.map do |study|
- assay_items = study.assays.map { |assay| build_assay_item(assay) }
- study_items << build_study_item(study, assay_items)
+ if investigation.is_isa_json_compliant?
+ investigation.studies.map do |study|
+ assay_stream_items = study.assay_streams.map do |assay_stream|
+ assay_items = assay_stream.child_assays.map do |child_assay|
+ build_assay_item(child_assay)
+ end
+ build_assay_stream_item(assay_stream, assay_items)
+ end
+
+ study_items << build_study_item(study, assay_stream_items)
+ end
+ else
+ investigation.studies.map do |study|
+ assay_items = study.assays.map { |assay| build_assay_item(assay) }
+ study_items << build_study_item(study, assay_items)
+ end
end
investigation_items << build_investigation_item(investigation, study_items)
study_items = []
@@ -125,6 +138,11 @@ def build_study_item(study, assay_items)
children: isa_study_elements(study) + assay_items, resource: study })
end
+ def build_assay_stream_item(assay_stream, child_assays)
+ create_node({ text: assay_stream.title, _type: 'assay', label: 'Assay Stream', _id: assay_stream.id, a_attr: BOLD,
+ children: isa_assay_elements(assay_stream) + child_assays, resource: assay_stream })
+ end
+
def build_assay_item(assay)
create_node({ text: assay.title, _type: 'assay', label: 'Assay', _id: assay.id, a_attr: BOLD,
children: isa_assay_elements(assay), resource: assay })
diff --git a/test/factories/assays.rb b/test/factories/assays.rb
index c0ab8efcf5..24f95f1ae0 100644
--- a/test/factories/assays.rb
+++ b/test/factories/assays.rb
@@ -13,6 +13,12 @@
description { "An experimental assay class description" }
end
+ factory(:assay_stream_class, class: AssayClass) do
+ title { I18n.t('assays.assay_stream') }
+ key { 'STREAM' }
+ description { "An assay stream class description" }
+ end
+
# SuggestedTechnologyType
factory(:suggested_technology_type) do
sequence(:label) { | n | "A TechnologyType#{n}" }
@@ -99,11 +105,20 @@
title { 'ISA JSON compliant assay' }
description { 'An assay linked to an ISA JSON compliant study and a sample type' }
after(:build) do |assay|
- assay.study = FactoryBot.create(:isa_json_compliant_study)
+ assay.study ||= FactoryBot.create(:isa_json_compliant_study)
assay.sample_type = FactoryBot.create(:isa_assay_material_sample_type, linked_sample_type: assay.study.sample_types.last)
end
end
+ factory(:assay_stream, parent: :assay_base) do
+ title { 'Assay Stream' }
+ description { 'A holder assay holding multiple child assays' }
+ association :assay_class, factory: :assay_stream_class
+ after(:build) do |assay|
+ assay.study ||= FactoryBot.create(:isa_json_compliant_study, contributor: assay.contributor)
+ end
+ end
+
# AssayAsset
factory :assay_asset do
association :assay
diff --git a/test/fixtures/assay_classes.yml b/test/fixtures/assay_classes.yml
index 6811c4bdb0..cf87a8f7d1 100644
--- a/test/fixtures/assay_classes.yml
+++ b/test/fixtures/assay_classes.yml
@@ -1,7 +1,11 @@
experimental_assay_class:
title: <%= I18n.t('assays.experimental_assay') %>
key: EXP
-
-modelling_assay_class:
+
+modelling_assay_class:
title: <%= I18n.t('assays.modelling_analysis') %>
- key: MODEL
\ No newline at end of file
+ key: MODEL
+
+assay_stream_class:
+ title: <%= I18n.t('assays.assay_stream') %>
+ key: STREAM
diff --git a/test/functional/assays_controller_test.rb b/test/functional/assays_controller_test.rb
index 77253f7191..1996d36918 100644
--- a/test/functional/assays_controller_test.rb
+++ b/test/functional/assays_controller_test.rb
@@ -587,14 +587,14 @@ def test_title
test 'get new with class doesnt present options for class' do
login_as(:model_owner)
- get :new, params: { class: 'experimental' }
+ get :new, params: { class: 'EXP' }
assert_response :success
assert_select 'a[href=?]', new_assay_path(class: :experimental), count: 0
assert_select 'a', text: /An #{I18n.t('assays.experimental_assay')}/i, count: 0
assert_select 'a[href=?]', new_assay_path(class: :modelling), count: 0
assert_select 'a', text: /A #{I18n.t('assays.modelling_analysis')}/i, count: 0
- get :new, params: { class: 'modelling' }
+ get :new, params: { class: 'MODEL' }
assert_response :success
assert_select 'a[href=?]', new_assay_path(class: :experimental), count: 0
assert_select 'a', text: /An #{I18n.t('assays.experimental_assay')}/i, count: 0
@@ -1065,7 +1065,7 @@ def check_fixtures_for_authorization_of_sops_and_datafiles_links
end
test 'new should include tags element' do
- get :new, params: { class: :experimental }
+ get :new, params: { class: 'EXP' }
assert_response :success
assert_select 'div.panel-heading', text: /Tags/, count: 1
assert_select 'select#tag_list', count: 1
@@ -1121,7 +1121,7 @@ def check_fixtures_for_authorization_of_sops_and_datafiles_links
end
test 'should show experimental assay types for new experimental assay' do
- get :new, params: { class: :experimental }
+ get :new, params: { class: 'EXP' }
assert_response :success
assert_select 'label', text: /assay type/i
assert_select 'select#assay_assay_type_uri' do
@@ -1131,7 +1131,7 @@ def check_fixtures_for_authorization_of_sops_and_datafiles_links
end
test 'should show modelling assay types for new modelling assay' do
- get :new, params: { class: :modelling }
+ get :new, params: { class: 'MODEL' }
assert_response :success
assert_select 'label', text: /Biological problem addressed/i
assert_select 'select#assay_assay_type_uri' do
@@ -1947,7 +1947,7 @@ def check_fixtures_for_authorization_of_sops_and_datafiles_links
# person = User.current_user.person
person = FactoryBot.create(:person)
project = person.projects.first
- investigation = FactoryBot.create(:investigation, projects: [project])
+ investigation = FactoryBot.create(:investigation, projects: [project], is_isa_json_compliant: true)
source_st = FactoryBot.create(:isa_source_sample_type, contributor: person, projects: [project])
sample_collection_st = FactoryBot.create(:isa_sample_collection_sample_type, contributor: person, projects: [project],
@@ -1984,7 +1984,7 @@ def check_fixtures_for_authorization_of_sops_and_datafiles_links
assay3.reload
- assert_equal(assay3.previous_linked_assay_sample_type&.id, assay1.sample_type&.id)
+ assert_equal(assay3.previous_linked_sample_type&.id, assay1.sample_type&.id)
end
test 'do not get index if feature disabled' do
@@ -2012,9 +2012,24 @@ def check_fixtures_for_authorization_of_sops_and_datafiles_links
with_config_value(:isa_json_compliance_enabled, true) do
current_user = FactoryBot.create(:user)
login_as(current_user)
- assay = FactoryBot.create(:isa_json_compliant_assay, contributor: current_user.person)
+ study = FactoryBot.create(:isa_json_compliant_study)
+ assay_stream = FactoryBot.create(:assay_stream, study: , contributor: current_user.person)
+ assay1 = FactoryBot.create(:isa_json_compliant_assay, contributor: current_user.person, study: , assay_stream:)
+ assay2 = FactoryBot.create(:isa_json_compliant_assay, contributor: current_user.person, study: , assay_stream:)
- get :show, params: { id: assay }
+ assert_equal assay_stream.study, assay1.study
+
+ get :show, params: { id: assay_stream }
+ assert_response :success
+
+ assert_select 'a', text: /Design #{I18n.t('assay')}/i, count: 1
+
+ get :show, params: { id: assay1 }
+ assert_response :success
+
+ assert_select 'a', text: /Design the next #{I18n.t('assay')}/i, count: 1
+
+ get :show, params: { id: assay2 }
assert_response :success
assert_select 'a', text: /Design the next #{I18n.t('assay')}/i, count: 1
diff --git a/test/functional/investigations_controller_test.rb b/test/functional/investigations_controller_test.rb
index a564eb29a4..b38571a183 100644
--- a/test/functional/investigations_controller_test.rb
+++ b/test/functional/investigations_controller_test.rb
@@ -911,16 +911,22 @@ def test_title
contributor: other_user.person)
# Create a 'private' assay in an assay stream
- assay_1_stream_1_sample_type = FactoryBot.create(:isa_assay_material_sample_type, linked_sample_type: sample_collection_sample_type, template_id: FactoryBot.create(:isa_assay_material_template).id)
- assay_1_stream_1 = FactoryBot.create(:assay, position: 0, sample_type: assay_1_stream_1_sample_type, study: accessible_study, contributor: current_user.person)
- assay_2_stream_1_sample_type = FactoryBot.create(:isa_assay_data_file_sample_type, linked_sample_type: assay_1_stream_1_sample_type, template_id: FactoryBot.create(:isa_assay_data_file_template).id)
- assay_2_stream_1 = FactoryBot.create(:assay, position:1, sample_type: assay_2_stream_1_sample_type, study: accessible_study, contributor: other_user.person)
+ stream_1 = FactoryBot.create(:assay_stream, title: 'Assay Stream 1', study: accessible_study, contributor: other_user.person)
+ assert_equal(stream_1.study, accessible_study)
+ assert(stream_1.is_assay_stream?)
+ assay_1_stream_1_sample_type = FactoryBot.create(:isa_assay_material_sample_type, contributor: other_user.person, linked_sample_type: sample_collection_sample_type, template_id: FactoryBot.create(:isa_assay_material_template).id)
+ assay_1_stream_1 = FactoryBot.create(:assay, position: 1, sample_type: assay_1_stream_1_sample_type, study: accessible_study, contributor: other_user.person, assay_stream_id: stream_1.id)
+ assay_2_stream_1_sample_type = FactoryBot.create(:isa_assay_data_file_sample_type, contributor: other_user.person, linked_sample_type: assay_1_stream_1_sample_type, template_id: FactoryBot.create(:isa_assay_data_file_template).id)
+ assay_2_stream_1 = FactoryBot.create(:assay, position: 2, sample_type: assay_2_stream_1_sample_type, study: accessible_study, contributor: other_user.person, assay_stream_id: stream_1.id)
# Create an assay stream with all assays visible
- assay_1_stream_2_sample_type = FactoryBot.create(:isa_assay_material_sample_type, linked_sample_type: sample_collection_sample_type, template_id: FactoryBot.create(:isa_assay_material_template).id)
- assay_1_stream_2 = FactoryBot.create(:assay, position: 0, sample_type: assay_1_stream_2_sample_type, study: accessible_study, contributor: current_user.person)
- assay_2_stream_2_sample_type = FactoryBot.create(:isa_assay_data_file_sample_type, linked_sample_type: assay_1_stream_2_sample_type, template_id: FactoryBot.create(:isa_assay_data_file_template).id)
- assay_2_stream_2 = FactoryBot.create(:assay, position:1, sample_type: assay_2_stream_2_sample_type, study: accessible_study, contributor: current_user.person)
+ stream_2 = FactoryBot.create(:assay_stream, title: 'Assay Stream 2', study: accessible_study, contributor: current_user.person)
+ assert_equal(stream_2.study, accessible_study)
+ assert(stream_2.is_assay_stream?)
+ assay_1_stream_2_sample_type = FactoryBot.create(:isa_assay_material_sample_type, contributor: current_user.person, linked_sample_type: sample_collection_sample_type, template_id: FactoryBot.create(:isa_assay_material_template).id)
+ assay_1_stream_2 = FactoryBot.create(:assay, position: 1, sample_type: assay_1_stream_2_sample_type, study: accessible_study, contributor: current_user.person, assay_stream_id: stream_2.id)
+ assay_2_stream_2_sample_type = FactoryBot.create(:isa_assay_data_file_sample_type, contributor: current_user.person, linked_sample_type: assay_1_stream_2_sample_type, template_id: FactoryBot.create(:isa_assay_data_file_template).id)
+ assay_2_stream_2 = FactoryBot.create(:assay, position: 2, sample_type: assay_2_stream_2_sample_type, study: accessible_study, contributor: current_user.person, assay_stream_id: stream_2.id)
# create samples in second assay stream with viewing permission
@@ -1096,8 +1102,13 @@ def test_title
assert json_investigation['studies'].map { |s| s['title'] }.include? accessible_study.title
study_json = json_investigation['studies'].first
+ # Total assays
+ assert_equal accessible_study.assays.count, 6
+ # Assay_streams
+ assert_equal accessible_study.assay_streams.count, 2
+ # Child assays
+ assert_equal accessible_study.assay_streams.map(&:child_assays).compact.flatten.count, 4
# Only one assay should end up in 1 assay stream in the ISA JSON
- assert_equal accessible_study.assays.count, 4
assert_equal study_json['assays'].count, 1
sample_ids = study_json['materials']['samples'].map { |sample| sample['@id'] }
diff --git a/test/functional/isa_assays_controller_test.rb b/test/functional/isa_assays_controller_test.rb
index f6b436f227..1212dd21d1 100644
--- a/test/functional/isa_assays_controller_test.rb
+++ b/test/functional/isa_assays_controller_test.rb
@@ -45,7 +45,8 @@ def setup
sop_ids: [FactoryBot.create(:sop, policy: FactoryBot.create(:public_policy)).id],
creator_ids: [this_person.id, other_creator.id],
other_creators: 'other collaborators',
- position: 0, assay_class_id: 1, policy_attributes: },
+ assay_class_id: AssayClass.experimental.id,
+ position: 0, policy_attributes: },
input_sample_type_id: sample_collection_sample_type.id,
sample_type: { title: 'assay sample_type', project_ids: [projects.first.id], template_id: 1,
sample_attributes_attributes: {
@@ -188,7 +189,7 @@ def setup
sop_ids: [FactoryBot.create(:sop, policy: FactoryBot.create(:public_policy)).id],
creator_ids: [this_person.id, other_creator.id],
other_creators: 'other collaborators',
- position: 0, assay_class_id: 1, policy_attributes: }
+ position: 0, assay_class_id: AssayClass.experimental.id, policy_attributes: }
isa_assay_attributes = { assay: assay_attributes.merge(emt_attributes),
input_sample_type_id: sample_collection_sample_type.id,
@@ -224,4 +225,60 @@ def setup
end
end
end
+
+ test 'hide sops, publications, documents, and discussion channels if assay stream' do
+ person = FactoryBot.create(:person)
+ study = FactoryBot.create(:isa_json_compliant_study, contributor: person)
+ assay_stream = FactoryBot.create(:assay_stream, study: , contributor: person)
+
+ get :new, params: {study_id: study.id, is_assay_stream: true}
+ assert_response :success
+
+ assert_select 'div#add_sops_form', text: /SOPs/i, count: 0
+ assert_select 'div#add_publications_form', text: /Publications/i, count: 0
+ assert_select 'div#add_documents_form', text: /Documents/i, count: 0
+ assert_select 'div.panel-heading', text: /Discussion Channels/i, count: 0
+ assert_select 'div.panel-heading', text: /Define Sample type for Assay/i, count: 0
+
+ get :edit, params: { id: assay_stream.id, study_id: study.id, source_assay_id: assay_stream.id, is_assay_stream: true }
+ assert_response :success
+
+ assert_select 'div#add_sops_form', text: /SOPs/i, count: 0
+ assert_select 'div#add_publications_form', text: /Publications/i, count: 0
+ assert_select 'div#add_documents_form', text: /Documents/i, count: 0
+ assert_select 'div.panel-heading', text: /Discussion Channels/i, count: 0
+ assert_select 'div.panel-heading', text: /Define Sample type for Assay/i, count: 0
+end
+
+ test 'show sops, publications, documents, and discussion channels if experimental assay' do
+ person = FactoryBot.create(:person)
+ project = person.projects.first
+ investigation = FactoryBot.create(:investigation, is_isa_json_compliant: true, contributor: person, projects: [project])
+ study = FactoryBot.create(:isa_json_compliant_study, contributor: person, investigation: )
+ assay_stream = FactoryBot.create(:assay_stream, study: , contributor: person, position: 0)
+
+ login_as(person)
+
+ get :new, params: {study_id: study.id, assay_stream_id: assay_stream.id, source_assay_id: assay_stream.id}
+ assert_response :success
+
+ assert_select 'div#add_sops_form', text: /SOPs/i, count: 1
+ assert_select 'div#add_publications_form', text: /Publications/i, count: 1
+ assert_select 'div#add_documents_form', text: /Documents/i, count:1
+ assert_select 'div.panel-heading', text: /Discussion Channels/i, count: 1
+ assert_select 'div.panel-heading', text: /Define Sample type for Assay/i, count: 1
+
+ first_assay_st = FactoryBot.create(:isa_assay_material_sample_type, contributor: person, projects: [project], linked_sample_type: study.sample_types.second)
+ first_assay = FactoryBot.create(:assay, contributor: person, study: , assay_stream: , position: 1, sample_type: first_assay_st)
+ assert_equal assay_stream, first_assay.assay_stream
+
+ get :edit, params: { id: first_assay.id, assay_stream_id: assay_stream.id, source_assay_id: first_assay.id, study_id: study.id }
+ assert_response :success
+
+ assert_select 'div#add_sops_form', text: /SOPs/i, count: 1
+ assert_select 'div#add_publications_form', text: /Publications/i, count: 1
+ assert_select 'div#add_documents_form', text: /Documents/i, count: 1
+ assert_select 'div.panel-heading', text: /Discussion Channels/i, count: 1
+ assert_select 'div.panel-heading', text: /Define Sample type for Assay/i, count: 1
+ end
end
diff --git a/test/functional/studies_controller_test.rb b/test/functional/studies_controller_test.rb
index 69a5ef4419..a29d1c8891 100644
--- a/test/functional/studies_controller_test.rb
+++ b/test/functional/studies_controller_test.rb
@@ -2022,7 +2022,7 @@ def test_should_show_investigation_tab
get :show, params: { id: study }
assert_response :success
- assert_select 'a', text: /Design #{I18n.t('assay')}/i, count: 1
+ assert_select 'a', text: /Design #{I18n.t('assay')} Stream/i, count: 1
assert_select 'a', text: /Add new #{I18n.t('assay')}/i, count: 0
end
end
diff --git a/test/unit/assay_class_test.rb b/test/unit/assay_class_test.rb
index 3a3d002f6d..3ef0593df8 100644
--- a/test/unit/assay_class_test.rb
+++ b/test/unit/assay_class_test.rb
@@ -4,12 +4,12 @@ class AssayClassTest < ActiveSupport::TestCase
# Replace this with your real tests.
fixtures :assay_classes
test 'for_type' do
- assert_equal 'EXP', AssayClass.for_type('experimental').key
- assert_equal 'MODEL', AssayClass.for_type('modelling').key
+ assert_equal 'EXP', AssayClass.experimental.key
+ assert_equal 'MODEL', AssayClass.modelling.key
end
test 'is_modelling?' do
- assert AssayClass.for_type('modelling').is_modelling?
- refute AssayClass.for_type('experimental').is_modelling?
+ assert AssayClass.modelling.is_modelling?
+ refute AssayClass.experimental.is_modelling?
end
end
diff --git a/test/unit/assay_test.rb b/test/unit/assay_test.rb
index a8a9199528..f72a1e7d50 100644
--- a/test/unit/assay_test.rb
+++ b/test/unit/assay_test.rb
@@ -754,10 +754,88 @@ def new_valid_assay
test 'isa json compliance' do
isa_json_compliant_study = FactoryBot.create(:isa_json_compliant_study)
assert isa_json_compliant_study.is_isa_json_compliant?
+
default_assay = FactoryBot.create(:assay, study: isa_json_compliant_study)
refute default_assay.is_isa_json_compliant?
- isa_json_compliant_assay = FactoryBot.create(:isa_json_compliant_assay)
+ assay_stream = FactoryBot.create(:assay_stream, study: isa_json_compliant_study)
+ assert assay_stream.is_isa_json_compliant?
+
+ isa_json_compliant_assay = FactoryBot.create(:isa_json_compliant_assay, study: isa_json_compliant_study)
assert isa_json_compliant_assay.is_isa_json_compliant?
end
+
+ test 'is assay stream' do
+ isa_json_compliant_study = FactoryBot.create(:isa_json_compliant_study)
+ assay_stream = FactoryBot.create(:assay_stream, study: isa_json_compliant_study)
+ assert assay_stream.is_assay_stream?
+
+ default_assay = FactoryBot.create(:assay)
+ refute default_assay.is_assay_stream?
+ end
+
+ test 'previous linked sample type' do
+ isa_study = FactoryBot.create(:isa_json_compliant_study)
+ def_study = FactoryBot.create(:study)
+
+ assay_stream = FactoryBot.create(:assay_stream, study: isa_study)
+ assert_equal assay_stream.previous_linked_sample_type, isa_study.sample_types.second
+
+ def_assay = FactoryBot.create(:assay, study:def_study)
+ assert_nil def_assay.previous_linked_sample_type
+
+ first_isa_assay = FactoryBot.create(:isa_json_compliant_assay,
+ assay_stream: ,
+ study: isa_study)
+ assert_equal first_isa_assay.previous_linked_sample_type, isa_study.sample_types.second
+
+ data_file_sample_type = FactoryBot.create(:isa_assay_data_file_sample_type,
+ linked_sample_type: first_isa_assay.sample_type)
+ second_isa_assay = FactoryBot.create(:assay,
+ study: isa_study,
+ assay_stream: ,
+ sample_type: data_file_sample_type)
+
+ assert_equal second_isa_assay.previous_linked_sample_type, first_isa_assay.sample_type
+ end
+
+ test 'has_linked_child_assay?' do
+ isa_study = FactoryBot.create(:isa_json_compliant_study)
+ def_study = FactoryBot.create(:study)
+ def_assay = FactoryBot.create(:assay, study:def_study)
+
+ assay_stream = FactoryBot.create(:assay_stream, study: isa_study)
+ first_isa_assay = FactoryBot.create(:isa_json_compliant_assay, study: isa_study)
+ data_file_sample_type = FactoryBot.create(:isa_assay_data_file_sample_type,
+ linked_sample_type: first_isa_assay.sample_type)
+ second_isa_assay = FactoryBot.create(:assay,
+ study: isa_study,
+ assay_stream: ,
+ sample_type: data_file_sample_type)
+
+ assert assay_stream.has_linked_child_assay?
+ refute def_assay.has_linked_child_assay?
+ assert first_isa_assay.has_linked_child_assay?
+ refute second_isa_assay.has_linked_child_assay?
+ end
+
+ test 'next_linked_child_assay' do
+ isa_study = FactoryBot.create(:isa_json_compliant_study)
+ def_study = FactoryBot.create(:study)
+ def_assay = FactoryBot.create(:assay, study:def_study)
+
+ assay_stream = FactoryBot.create(:assay_stream, study: isa_study)
+ first_isa_assay = FactoryBot.create(:isa_json_compliant_assay, study: isa_study)
+ data_file_sample_type = FactoryBot.create(:isa_assay_data_file_sample_type,
+ linked_sample_type: first_isa_assay.sample_type)
+ second_isa_assay = FactoryBot.create(:assay,
+ study: isa_study,
+ assay_stream: ,
+ sample_type: data_file_sample_type)
+
+ assert_equal assay_stream.next_linked_child_assay, first_isa_assay
+ assert_nil def_assay.next_linked_child_assay
+ assert_equal first_isa_assay.next_linked_child_assay, second_isa_assay
+ assert_nil second_isa_assay.next_linked_child_assay
+ end
end
diff --git a/test/unit/ontologies/ontology_synchronization_test.rb b/test/unit/ontologies/ontology_synchronization_test.rb
index 50aeb1c446..06ba1d31df 100644
--- a/test/unit/ontologies/ontology_synchronization_test.rb
+++ b/test/unit/ontologies/ontology_synchronization_test.rb
@@ -3,10 +3,10 @@
class OntologySynchronizationTest < ActiveSupport::TestCase
def setup
- unless AssayClass.for_type('modelling')
+ unless AssayClass.modelling
FactoryBot.create(:modelling_assay_class)
end
- unless AssayClass.for_type('experimental')
+ unless AssayClass.experimental
FactoryBot.create(:experimental_assay_class)
end
end