From 5ea5acb645a177165fe8f4928b69a604cfcc0344 Mon Sep 17 00:00:00 2001 From: Stuart Owen Date: Wed, 8 Nov 2023 14:27:17 +0000 Subject: [PATCH 01/10] remove Ontology attribute type from seeds #1632 --- db/seeds/007_sample_attribute_types.seeds.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/db/seeds/007_sample_attribute_types.seeds.rb b/db/seeds/007_sample_attribute_types.seeds.rb index 094c3a19eb..69cc5462d1 100644 --- a/db/seeds/007_sample_attribute_types.seeds.rb +++ b/db/seeds/007_sample_attribute_types.seeds.rb @@ -79,9 +79,6 @@ data_file_type = SampleAttributeType.find_or_initialize_by(title: 'Registered Data file') data_file_type.update(base_type: Seek::Samples::BaseType::SEEK_DATA_FILE) -ontology_type = SampleAttributeType.find_or_initialize_by(title:'Ontology') -ontology_type.update(base_type: Seek::Samples::BaseType::CV) - cv_list_type = SampleAttributeType.find_or_initialize_by(title:'Controlled Vocabulary List') cv_list_type.update(base_type: Seek::Samples::BaseType::CV_LIST) From 7eb19f943930cef8c6b9ecd355b2086423d0b0b4 Mon Sep 17 00:00:00 2001 From: Stuart Owen Date: Wed, 8 Nov 2023 14:46:41 +0000 Subject: [PATCH 02/10] add SampleAttributeType->TemplateAttributes association #1632 added as isa_template_attributes, to be consistent with SampleType.isa_template (to avoid clash with general use of template) --- app/models/sample_attribute_type.rb | 1 + test/unit/sample_attribute_type_test.rb | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/app/models/sample_attribute_type.rb b/app/models/sample_attribute_type.rb index af109086a0..1af364de92 100644 --- a/app/models/sample_attribute_type.rb +++ b/app/models/sample_attribute_type.rb @@ -4,6 +4,7 @@ class SampleAttributeType < ApplicationRecord validate :validate_allowed_type, :validate_regular_expression, :validate_resolution has_many :sample_attributes, inverse_of: :sample_attribute_type + has_many :isa_template_attributes, class_name: 'TemplateAttribute', inverse_of: :sample_attribute_type has_many :custom_metadata_attributes, inverse_of: :sample_attribute_type before_save :set_defaults_attributes diff --git a/test/unit/sample_attribute_type_test.rb b/test/unit/sample_attribute_type_test.rb index 1ecdc5e6d7..eee6d509ad 100644 --- a/test/unit/sample_attribute_type_test.rb +++ b/test/unit/sample_attribute_type_test.rb @@ -247,4 +247,22 @@ class SampleAttributeTypeTest < ActiveSupport::TestCase type = FactoryBot.create(:controlled_vocab_attribute_type) refute type.seek_data_file? end + + test 'isa_template_attributes' do + type = FactoryBot.create(:age_sample_attribute_type) + type2 = FactoryBot.create(:weight_sample_attribute_type) + + ta1 = FactoryBot.create(:template_attribute, sample_attribute_type: type) + ta2 = FactoryBot.create(:template_attribute, sample_attribute_type: type) + ta3 = FactoryBot.create(:template_attribute, sample_attribute_type: type) + ta4 = FactoryBot.create(:template_attribute, sample_attribute_type: type2) + + type.reload + template_attributes = type.isa_template_attributes + assert_equal 3, template_attributes.count + assert_includes template_attributes, ta1 + assert_includes template_attributes, ta2 + assert_includes template_attributes, ta3 + assert_not_includes template_attributes, ta4 + end end From f5b8e1ba42d1cf0056214ff71be9e8c6985e852f Mon Sep 17 00:00:00 2001 From: Stuart Owen Date: Wed, 8 Nov 2023 15:29:42 +0000 Subject: [PATCH 03/10] upgrade task to update and remove Ontology attribute type #1632 moves sample and template attributes to CV, and then deletes the ontology one. Only deletes if both Ontology and CV attribute types are found --- lib/tasks/seek_upgrades.rake | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/lib/tasks/seek_upgrades.rake b/lib/tasks/seek_upgrades.rake index 20c77f614a..4d73ab8903 100644 --- a/lib/tasks/seek_upgrades.rake +++ b/lib/tasks/seek_upgrades.rake @@ -9,6 +9,7 @@ namespace :seek do task upgrade_version_tasks: %i[ environment strip_sample_attribute_pids + remove_ontology_attribute_type ] # these are the tasks that are executes for each upgrade as standard, and rarely change @@ -58,6 +59,32 @@ namespace :seek do puts "... Finished stripping #{n} Sample Attribute PIds." end + task(remove_ontology_attribute_type: [:environment]) do + ontology_attr_type = SampleAttributeType.find_by(title:'Ontology') + cv_attr_type = SampleAttributeType.find_by(title:'Controlled Vocabulary') + if ontology_attr_type + puts '..... Removing the Ontology sample attribute type ...' + if cv_attr_type + if ontology_attr_type.sample_attributes.any? + puts "..... Moving #{ontology_attr_type.sample_attributes.count} sample attributes to Controlled Vocabulary" + ontology_attr_type.sample_attributes.each do |attr_type| + attr_type.update_column(:sample_attribute_type_id, cv_attr_type.id) + end + end + if ontology_attr_type.isa_template_attributes.any? + puts "..... Moving #{ontology_attr_type.isa_template_attributes.count} template attributes to Controlled Vocabulary" + ontology_attr_type.isa_template_attributes.each do |attr_type| + attr_type.update_column(:sample_attribute_type_id, cv_attr_type.id) + end + end + + ontology_attr_type.destroy + else + puts '..... Target Controlled Vocabulary attribute not found' + end + end + end + private ## From d99d0d4a15ff9676901adf87ce1dd8684d7949e0 Mon Sep 17 00:00:00 2001 From: Stuart Owen Date: Wed, 8 Nov 2023 15:52:58 +0000 Subject: [PATCH 04/10] removed the old use of `data-is-ontology` related javascript #1632 lead to some odd behaviour, and was based on custom_input rather than being based on an ontology --- app/assets/javascripts/sample_types.js | 2 -- app/models/sample_attribute_type.rb | 4 ---- app/views/sample_types/_sample_attribute_form.html.erb | 4 ++-- app/views/templates/_template_attribute_form.erb | 4 ++-- 4 files changed, 4 insertions(+), 10 deletions(-) diff --git a/app/assets/javascripts/sample_types.js b/app/assets/javascripts/sample_types.js index 2f832bed9f..1d933fd260 100644 --- a/app/assets/javascripts/sample_types.js +++ b/app/assets/javascripts/sample_types.js @@ -72,12 +72,10 @@ var SampleTypes = { attributeTypeChanged: function (e, resetSelection=true) { //check if it is a controlled vocab, and change the state of the controlled vocab selector if need be var use_cv = $j(this).find(':selected').data('use-cv'); - var is_ontology = $j(this).find(':selected').data('is-ontology'); var cv_element = $j(this).siblings('.controlled-vocab-block'); if (use_cv) { var cv_selection = cv_element.find('.controlled-vocab-selection'); cv_selection.find('option').show(); - cv_selection.find(`option[data-is-ontology="${!is_ontology}"]`).hide(); if (resetSelection) cv_selection.find('option:selected').prop("selected", false); cv_element.show(); } diff --git a/app/models/sample_attribute_type.rb b/app/models/sample_attribute_type.rb index 1af364de92..93bd5d9faa 100644 --- a/app/models/sample_attribute_type.rb +++ b/app/models/sample_attribute_type.rb @@ -109,10 +109,6 @@ def seek_data_file? base_type == Seek::Samples::BaseType::SEEK_DATA_FILE end - def ontology? - controlled_vocab? && title == 'Ontology' - end - def base_type_handler(additional_options = {}) Seek::Samples::AttributeTypeHandlers::AttributeTypeHandlerFactory.instance.for_base_type(base_type, additional_options) end diff --git a/app/views/sample_types/_sample_attribute_form.html.erb b/app/views/sample_types/_sample_attribute_form.html.erb index 0f9cbb1e19..fef47dc1b8 100644 --- a/app/views/sample_types/_sample_attribute_form.html.erb +++ b/app/views/sample_types/_sample_attribute_form.html.erb @@ -52,13 +52,13 @@ <%= select_tag "#{field_name_prefix}[sample_attribute_type_id]", - options_for_select(displayed_sample_attribute_types.sort_by(&:title).sort_by { |t| t.default? ? 0 : 1 }.map { |t| [t.title, t.id,{'data-use-cv'=>t.controlled_vocab? || t.seek_cv_list?,'data-is-ontology'=>t.ontology?, 'data-is-seek-sample'=>t.seek_sample? || t.seek_sample_multi? }] }, attribute_type_id), + options_for_select(displayed_sample_attribute_types.sort_by(&:title).sort_by { |t| t.default? ? 0 : 1 }.map { |t| [t.title, t.id,{'data-use-cv'=>t.controlled_vocab? || t.seek_cv_list?, 'data-is-seek-sample'=>t.seek_sample? || t.seek_sample_multi? }] }, attribute_type_id), class: 'form-control sample-type-attribute-type', disabled: !allow_type_change, data: { attr: "type" } %>

<%= select_tag "#{field_name_prefix}[sample_controlled_vocab_id]", - options_for_select(SampleControlledVocab.all.map { |scv| [scv.title, scv.id, {'data-editable'=>scv.can_edit?, 'data-is-ontology'=>scv.custom_input?}] }, + options_for_select(SampleControlledVocab.all.map { |scv| [scv.title, scv.id, {'data-editable'=>scv.can_edit?}] }, sample_controlled_vocab_id), include_blank: true, class: 'form-control controlled-vocab-selection', diff --git a/app/views/templates/_template_attribute_form.erb b/app/views/templates/_template_attribute_form.erb index a1b834408d..c2563a58a6 100644 --- a/app/views/templates/_template_attribute_form.erb +++ b/app/views/templates/_template_attribute_form.erb @@ -42,13 +42,13 @@ <%= select_tag "#{field_name_prefix}[sample_attribute_type_id]", - options_for_select(SampleAttributeType.all.sort_by(&:title).sort_by { |t| t.default? ? 0 : 1 }.map { |t| [t.title, t.id,{'data-is-cv'=>t.controlled_vocab?,'data-is-ontology'=>t.ontology?, 'data-is-seek-sample'=>t.seek_sample? || t.seek_sample_multi? }] }, attribute_type_id), + options_for_select(SampleAttributeType.all.sort_by(&:title).sort_by { |t| t.default? ? 0 : 1 }.map { |t| [t.title, t.id,{'data-is-cv'=>t.controlled_vocab?,'data-is-seek-sample'=>t.seek_sample? || t.seek_sample_multi? }] }, attribute_type_id), class: 'form-control sample-type-attribute-type', data: {attr: "type"} %>

<%= select_tag "#{field_name_prefix}[sample_controlled_vocab_id]", - options_for_select(SampleControlledVocab.all.map { |scv| [scv.title, scv.id, {'data-editable'=>scv.can_edit?, 'data-is-ontology'=>scv.custom_input?}] }, + options_for_select(SampleControlledVocab.all.map { |scv| [scv.title, scv.id, {'data-editable'=>scv.can_edit?}] }, sample_controlled_vocab_id),include_blank: true, class: 'form-control controlled-vocab-selection', data: {attr: "cv_id"} %> From b4f20c73eee5a8c8f72d402603bd2158445f23fb Mon Sep 17 00:00:00 2001 From: Stuart Owen Date: Wed, 8 Nov 2023 10:47:44 +0000 Subject: [PATCH 05/10] renamed Registered Sample (multiple) to Registered Sample List #1636 for consistency --- db/seeds/007_sample_attribute_types.seeds.rb | 14 +++++++------- lib/tasks/seek_upgrades.rake | 13 +++++++++++-- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/db/seeds/007_sample_attribute_types.seeds.rb b/db/seeds/007_sample_attribute_types.seeds.rb index 094c3a19eb..ec5cf04200 100644 --- a/db/seeds/007_sample_attribute_types.seeds.rb +++ b/db/seeds/007_sample_attribute_types.seeds.rb @@ -61,12 +61,18 @@ seek_sample_type = SampleAttributeType.find_or_initialize_by(title:'Registered Sample') seek_sample_type.update(base_type: Seek::Samples::BaseType::SEEK_SAMPLE) -seek_sample_multi_type = SampleAttributeType.find_or_initialize_by(title:'Registered Sample (multiple)') +seek_sample_multi_type = SampleAttributeType.find_or_initialize_by(title:'Registered Sample List') seek_sample_multi_type.update(base_type: Seek::Samples::BaseType::SEEK_SAMPLE_MULTI) +ontology_type = SampleAttributeType.find_or_initialize_by(title:'Ontology') +ontology_type.update(base_type: Seek::Samples::BaseType::CV) + cv_type = SampleAttributeType.find_or_initialize_by(title:'Controlled Vocabulary') cv_type.update(base_type: Seek::Samples::BaseType::CV) +cv_list_type = SampleAttributeType.find_or_initialize_by(title:'Controlled Vocabulary List') +cv_list_type.update(base_type: Seek::Samples::BaseType::CV_LIST) + uri_type = SampleAttributeType.find_or_initialize_by(title:'URI') uri_type.update(base_type: Seek::Samples::BaseType::STRING, regexp: URI.regexp.to_s, placeholder: 'http://www.example.com/123', resolution:'\\0') @@ -79,12 +85,6 @@ data_file_type = SampleAttributeType.find_or_initialize_by(title: 'Registered Data file') data_file_type.update(base_type: Seek::Samples::BaseType::SEEK_DATA_FILE) -ontology_type = SampleAttributeType.find_or_initialize_by(title:'Ontology') -ontology_type.update(base_type: Seek::Samples::BaseType::CV) - -cv_list_type = SampleAttributeType.find_or_initialize_by(title:'Controlled Vocabulary List') -cv_list_type.update(base_type: Seek::Samples::BaseType::CV_LIST) - linked_custom_metadata_type = SampleAttributeType.find_or_initialize_by(title:'Linked Custom Metadata') linked_custom_metadata_type.update(base_type: Seek::Samples::BaseType::LINKED_CUSTOM_METADATA) diff --git a/lib/tasks/seek_upgrades.rake b/lib/tasks/seek_upgrades.rake index 20c77f614a..b2f18cc399 100644 --- a/lib/tasks/seek_upgrades.rake +++ b/lib/tasks/seek_upgrades.rake @@ -9,6 +9,7 @@ namespace :seek do task upgrade_version_tasks: %i[ environment strip_sample_attribute_pids + rename_registered_sample_multiple_attribute_type ] # these are the tasks that are executes for each upgrade as standard, and rarely change @@ -45,8 +46,16 @@ namespace :seek do end end + task(rename_registered_sample_multiple_attribute_type: [:environment]) do + attr = SampleAttributeType.find_by(title:'Registered Sample (multiple)') + if attr + puts "..... Renaming sample attribute type 'Registered Sample (multiple)' to 'Registered Sample List'." + attr.update_column(:title, 'Registered Sample List') + end + end + task(strip_sample_attribute_pids: [:environment]) do - puts '... Stripping Sample Attribute PIds ...' + puts '..... Stripping Sample Attribute PIds ...' n = 0 SampleAttribute.where('pid is NOT NULL AND pid !=?','').each do |attribute| new_pid = attribute.pid.strip @@ -55,7 +64,7 @@ namespace :seek do n += 1 end end - puts "... Finished stripping #{n} Sample Attribute PIds." + puts "..... Finished stripping #{n} Sample Attribute PIds." end private From 77d78d98f6dea9a50c50e3f1deb086156d285c1c Mon Sep 17 00:00:00 2001 From: Stuart Owen Date: Wed, 8 Nov 2023 17:10:45 +0000 Subject: [PATCH 06/10] fix to how isa-exporter deterimnes whether an attribute is ontology based #1632 --- app/models/sample_attribute.rb | 5 +++++ lib/isa_exporter.rb | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/models/sample_attribute.rb b/app/models/sample_attribute.rb index 6b2c413604..4e31d83aeb 100644 --- a/app/models/sample_attribute.rb +++ b/app/models/sample_attribute.rb @@ -59,6 +59,11 @@ def linked_custom_metadata_type nil end + # whether this attribute is tied to a controlled vocab which has a source ontology + def ontology_based? + controlled_vocab? && sample_controlled_vocab&.source_ontology.present? + end + private def store_accessor_name diff --git a/lib/isa_exporter.rb b/lib/isa_exporter.rb index 8f550f57de..5971017cbe 100644 --- a/lib/isa_exporter.rb +++ b/lib/isa_exporter.rb @@ -173,7 +173,7 @@ def convert_ontologies sample_types = @investigation.studies.map(&:sample_types) + @investigation.assays.map(&:sample_type) sample_types.flatten.each do |sa| sa.sample_attributes.each do |atr| - source_ontologies << atr.sample_controlled_vocab.source_ontology if atr.sample_attribute_type.ontology? + source_ontologies << atr.sample_controlled_vocab.source_ontology if atr.ontology_based? end end source_ontologies.uniq.map { |s| { name: s, file: '', version: '', description: '' } } @@ -487,7 +487,7 @@ def extract_sample_ids(obj, type) end def get_ontology_details(sample_attribute, label, vocab_term) - is_ontology = sample_attribute.sample_attribute_type.ontology? + is_ontology = sample_attribute.ontology_based? iri = '' if is_ontology iri = From e33ec14b3d13b038d473f2bbb334f46d4faba01a Mon Sep 17 00:00:00 2001 From: Stuart Owen Date: Thu, 9 Nov 2023 10:30:18 +0000 Subject: [PATCH 07/10] test for ontology_based? and refactor to use delegation #1632 --- app/models/sample_attribute.rb | 8 +++----- test/unit/sample_attribute_test.rb | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/app/models/sample_attribute.rb b/app/models/sample_attribute.rb index 4e31d83aeb..62386928d2 100644 --- a/app/models/sample_attribute.rb +++ b/app/models/sample_attribute.rb @@ -22,6 +22,9 @@ class SampleAttribute < ApplicationRecord # sample type exists attr_reader :deferred_link_to_self + # whether this attribute is tied to a controlled vocab which has a source ontology + delegate :ontology_based?, to: :sample_controlled_vocab, allow_nil: true + def title=(title) super store_accessor_name @@ -59,11 +62,6 @@ def linked_custom_metadata_type nil end - # whether this attribute is tied to a controlled vocab which has a source ontology - def ontology_based? - controlled_vocab? && sample_controlled_vocab&.source_ontology.present? - end - private def store_accessor_name diff --git a/test/unit/sample_attribute_test.rb b/test/unit/sample_attribute_test.rb index 48085edd73..4270b2e7b8 100644 --- a/test/unit/sample_attribute_test.rb +++ b/test/unit/sample_attribute_test.rb @@ -330,6 +330,22 @@ class SampleAttributeTest < ActiveSupport::TestCase assert_equal '', attribute.short_pid end + test 'ontology_based?' do + attribute = FactoryBot.create(:sample_sample_attribute, sample_type: FactoryBot.create(:simple_sample_type)) + refute attribute.ontology_based? + + attribute = FactoryBot.create(:simple_string_sample_attribute, sample_type: FactoryBot.create(:simple_sample_type)) + refute attribute.ontology_based? + + attribute = FactoryBot.create(:apples_controlled_vocab_attribute, sample_type: FactoryBot.create(:simple_sample_type)) + refute attribute.sample_controlled_vocab.ontology_based? + refute attribute.ontology_based? + + attribute.sample_controlled_vocab = FactoryBot.create(:topics_controlled_vocab) + assert attribute.sample_controlled_vocab.ontology_based? + assert attribute.ontology_based? + end + private def valid_value?(attribute, value) From 6cbf86dde93bad831d7c5378ba5451920845c2df Mon Sep 17 00:00:00 2001 From: Stuart Owen Date: Thu, 9 Nov 2023 10:51:45 +0000 Subject: [PATCH 08/10] minor rewording of code comment #1632 --- app/models/sample_attribute.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/sample_attribute.rb b/app/models/sample_attribute.rb index 62386928d2..bc895e6971 100644 --- a/app/models/sample_attribute.rb +++ b/app/models/sample_attribute.rb @@ -22,7 +22,7 @@ class SampleAttribute < ApplicationRecord # sample type exists attr_reader :deferred_link_to_self - # whether this attribute is tied to a controlled vocab which has a source ontology + # whether this attribute is tied to a controlled vocab which is based on an ontology delegate :ontology_based?, to: :sample_controlled_vocab, allow_nil: true def title=(title) From 8d9afc62dd04f45a57f2a2a41d121be20647ff11 Mon Sep 17 00:00:00 2001 From: Stuart Owen Date: Thu, 9 Nov 2023 10:53:34 +0000 Subject: [PATCH 09/10] fix upgrade task log message #1632 --- lib/tasks/seek_upgrades.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/seek_upgrades.rake b/lib/tasks/seek_upgrades.rake index 2346e763e0..1ba911d459 100644 --- a/lib/tasks/seek_upgrades.rake +++ b/lib/tasks/seek_upgrades.rake @@ -89,7 +89,7 @@ namespace :seek do ontology_attr_type.destroy else - puts '..... Target Controlled Vocabulary attribute not found' + puts '..... Target Controlled Vocabulary attribute type not found' end end end From 8bef10a891b035067f0bd61aee86d9774b94ead0 Mon Sep 17 00:00:00 2001 From: Stuart Owen Date: Thu, 9 Nov 2023 13:37:57 +0000 Subject: [PATCH 10/10] cleaned up some old =>'s from effected views #1632 --- app/views/sample_types/_sample_attribute_form.html.erb | 6 ++++-- app/views/templates/_template_attribute_form.erb | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/views/sample_types/_sample_attribute_form.html.erb b/app/views/sample_types/_sample_attribute_form.html.erb index fef47dc1b8..70ad21cda2 100644 --- a/app/views/sample_types/_sample_attribute_form.html.erb +++ b/app/views/sample_types/_sample_attribute_form.html.erb @@ -52,13 +52,15 @@ <%= select_tag "#{field_name_prefix}[sample_attribute_type_id]", - options_for_select(displayed_sample_attribute_types.sort_by(&:title).sort_by { |t| t.default? ? 0 : 1 }.map { |t| [t.title, t.id,{'data-use-cv'=>t.controlled_vocab? || t.seek_cv_list?, 'data-is-seek-sample'=>t.seek_sample? || t.seek_sample_multi? }] }, attribute_type_id), + options_for_select(displayed_sample_attribute_types.sort_by(&:title).sort_by { |t| t.default? ? 0 : 1 }.map do |t| + [t.title, t.id,{'data-use-cv': t.controlled_vocab? || t.seek_cv_list?, 'data-is-seek-sample': t.seek_sample? || t.seek_sample_multi? }] + end , attribute_type_id), class: 'form-control sample-type-attribute-type', disabled: !allow_type_change, data: { attr: "type" } %>

<%= select_tag "#{field_name_prefix}[sample_controlled_vocab_id]", - options_for_select(SampleControlledVocab.all.map { |scv| [scv.title, scv.id, {'data-editable'=>scv.can_edit?}] }, + options_for_select(SampleControlledVocab.all.map { |scv| [scv.title, scv.id, {'data-editable': scv.can_edit?}] }, sample_controlled_vocab_id), include_blank: true, class: 'form-control controlled-vocab-selection', diff --git a/app/views/templates/_template_attribute_form.erb b/app/views/templates/_template_attribute_form.erb index c2563a58a6..7047719114 100644 --- a/app/views/templates/_template_attribute_form.erb +++ b/app/views/templates/_template_attribute_form.erb @@ -42,13 +42,15 @@ <%= select_tag "#{field_name_prefix}[sample_attribute_type_id]", - options_for_select(SampleAttributeType.all.sort_by(&:title).sort_by { |t| t.default? ? 0 : 1 }.map { |t| [t.title, t.id,{'data-is-cv'=>t.controlled_vocab?,'data-is-seek-sample'=>t.seek_sample? || t.seek_sample_multi? }] }, attribute_type_id), + options_for_select(SampleAttributeType.all.sort_by(&:title).sort_by { |t| t.default? ? 0 : 1 }.map do |t| + [t.title, t.id,{'data-is-cv': t.controlled_vocab?,'data-is-seek-sample': t.seek_sample? || t.seek_sample_multi? }] + end, attribute_type_id), class: 'form-control sample-type-attribute-type', data: {attr: "type"} %>

<%= select_tag "#{field_name_prefix}[sample_controlled_vocab_id]", - options_for_select(SampleControlledVocab.all.map { |scv| [scv.title, scv.id, {'data-editable'=>scv.can_edit?}] }, + options_for_select(SampleControlledVocab.all.map { |scv| [scv.title, scv.id, {'data-editable': scv.can_edit?}] }, sample_controlled_vocab_id),include_blank: true, class: 'form-control controlled-vocab-selection', data: {attr: "cv_id"} %>