diff --git a/app/assets/javascripts/sample_types.js b/app/assets/javascripts/sample_types.js index 298f65396d..39306a8d51 100644 --- a/app/assets/javascripts/sample_types.js +++ b/app/assets/javascripts/sample_types.js @@ -96,6 +96,16 @@ var SampleTypes = { else { seek_sample_element.hide(); } + }, + + expandIRI: function () { + var termIRI = $j(this).next('.term-iri'); + termIRI.toggleClass('visible'); } }; + + +$j(document).ready(function() { + $j('.term-label').on('click', SampleTypes.expandIRI); +}); \ No newline at end of file diff --git a/app/assets/javascripts/upload.js b/app/assets/javascripts/upload.js index e5a0c8c213..c4a2b1eec9 100644 --- a/app/assets/javascripts/upload.js +++ b/app/assets/javascripts/upload.js @@ -69,6 +69,7 @@ $j(document).ready(function () { filenameInput.val(''); $j('[data-role="seek-url-checker-msg-success"]', field).hide(); $j('[data-role="seek-url-checker-msg-too-big"]', field).hide(); + $j('[role="seek-url-checker-remind-to-add-file"]',field).hide(); pending.append(HandlebarsTemplates['upload/remote_file'](remoteFile)); } }; @@ -92,6 +93,7 @@ $j(document).ready(function () { var result = field.find('[data-role="seek-url-checker-result"]'); var copyDialog = $j('[data-role="seek-url-checker-msg-success"]', field); var tooBig = $j('[data-role="seek-url-checker-msg-too-big"]', field); + var addReminder = $j('[role="seek-url-checker-remind-to-add-file"]',field); var submitUrl = function () { result.html('').spinner('add'); @@ -111,6 +113,7 @@ $j(document).ready(function () { checker.trigger('urlChecked', [info]); if (info.allow_copy) { copyDialog.show(); + addReminder.show(); } else { tooBig.show(); } diff --git a/app/assets/stylesheets/linked_extended_metadata.css b/app/assets/stylesheets/linked_extended_metadata.css index fee60b92ba..e84740e215 100644 --- a/app/assets/stylesheets/linked_extended_metadata.css +++ b/app/assets/stylesheets/linked_extended_metadata.css @@ -1,3 +1,21 @@ +.term-label { + font-weight: normal; + text-decoration: underline; +} + +.term-iri { + display: none; + background-color: rgba(177, 183, 187, 0.47); + color: black; + font-weight: normal; +} + +.term-iri.visible { + display: inline; + opacity: 1; + margin-left: 10px; +} + .linked_extended_metdata .panel-default .panel-heading { font-weight: bold; color: #333; diff --git a/app/controllers/samples_controller.rb b/app/controllers/samples_controller.rb index d2289dccbb..cde6c4d694 100644 --- a/app/controllers/samples_controller.rb +++ b/app/controllers/samples_controller.rb @@ -220,31 +220,45 @@ def typeahead def query project_ids = params[:project_ids]&.map(&:to_i) - + attribute_filter_value = params[:template_attribute_value]&.downcase @result = params[:template_id].present? ? Template.find(params[:template_id]).sample_types.map(&:samples).flatten : [] - if params[:template_attribute_id].present? && params[:template_attribute_value].present? - attribute_title = TemplateAttribute.find(params[:template_attribute_id]).title - @result = @result.select { |s| s.get_attribute_value(attribute_title)&.include?(params[:template_attribute_value]) } + if params[:template_attribute_id].present? && attribute_filter_value.present? + template_attribute = TemplateAttribute.find(params[:template_attribute_id]) + @result = @result.select do |s| + sample_attribute = s.sample_type.sample_attributes.detect { |sa| template_attribute.sample_attributes.include? sa } + sample_attribute_title = sample_attribute&.title + if sample_attribute&.sample_attribute_type&.seek_sample_multi? + attr_value = s.get_attribute_value(sample_attribute_title) + attr_value&.any? { |v| v[:title].downcase.include?(attribute_filter_value) } + elsif sample_attribute&.sample_attribute_type&.seek_sample? + s.get_attribute_value(sample_attribute_title)[:title]&.downcase&.include?(attribute_filter_value) + elsif sample_attribute&.sample_attribute_type&.seek_cv_list? + attr_value = s.get_attribute_value(sample_attribute_title) + attr_value&.any? { |v| v.downcase.include?(attribute_filter_value) } + else + s.get_attribute_value(sample_attribute_title)&.downcase&.include?(attribute_filter_value) + end + end end if params[:input_template_id].present? # linked - title = - TemplateAttribute.find(params[:input_attribute_id]).title if params[:input_attribute_id].present? - @result = find_samples(@result, :linked_samples, - { attribute_id: params[:input_attribute_id], + input_template_attribute = + TemplateAttribute.find(params[:input_attribute_id]) + @result = filter_linked_samples(@result, :linked_samples, + { attribute_id: params[:input_attribute_id], attribute_value: params[:input_attribute_value], - template_id: params[:input_template_id] }, title) + template_id: params[:input_template_id] }, input_template_attribute) end if params[:output_template_id].present? # linking - title = - TemplateAttribute.find(params[:output_attribute_id]).title if params[:output_attribute_id].present? - @result = find_samples(@result, :linking_samples, - { attribute_id: params[:output_attribute_id], + output_template_attribute = + TemplateAttribute.find(params[:output_attribute_id]) + @result = filter_linked_samples(@result, :linking_samples, + { attribute_id: params[:output_attribute_id], attribute_value: params[:output_attribute_value], - template_id: params[:output_template_id] }, title) + template_id: params[:output_template_id] }, output_template_attribute) end @result = @result.select { |s| (project_ids & s.project_ids).any? } if project_ids.present? @@ -318,16 +332,36 @@ def find_index_assets end end - def find_samples(samples, link, options, title) + # Filters linked samples based on the provided options and template attribute title. + # + # @param samples [Array] the list of samples to filter + # @param link [Symbol] the method to call on each sample to get the linked samples (:linked_samples or :linking_samples) + # @param options [Hash] the options for filtering + # @option options [Integer] :template_id the ID of the template to filter by + # @option options [String] :attribute_value the value of the attribute to filter by + # @option options [Integer] :attribute_id the ID of the attribute to filter by + # @param template_attribute_title [String] the title of the template attribute to filter by + # @return [Array] the filtered list of samples + def filter_linked_samples(samples, link, options, template_attribute) + raise ArgumentError, "Invalid linking method provided. '#{link.to_s}' is not allowed!" unless %i[linked_samples linking_samples].include? link + + template_attribute_title = template_attribute&.title samples.select do |s| s.send(link).any? do |x| selected = x.sample_type.template_id == options[:template_id].to_i - selected = x.get_attribute_value(title)&.include?(options[:attribute_value]) if title.present? && selected - selected || find_samples([x], link, options, title).present? + if template_attribute.sample_attribute_type.seek_sample_multi? + selected = x.get_attribute_value(template_attribute_title)&.any? { |v| v[:title].downcase.include?(options[:attribute_value]) } if template_attribute.present? && selected + elsif template_attribute.sample_attribute_type.seek_sample? + selected = x.get_attribute_value(template_attribute_title)&[:title].downcase&.include?(options[:attribute_value]) if template_attribute.present? && selected + elsif template_attribute.sample_attribute_type.seek_cv_list? + selected = x.get_attribute_value(template_attribute_title)&.any? { |v| v.downcase.include?(options[:attribute_value]) } if template_attribute.present? && selected + else + selected = x.get_attribute_value(template_attribute_title)&.downcase&.include?(options[:attribute_value]&.downcase) if template_attribute.present? && selected + end + selected || filter_linked_samples([x], link, options, template_attribute).present? end end end - def templates_enabled? unless Seek::Config.isa_json_compliance_enabled flash[:error] = 'Not available' diff --git a/app/controllers/templates_controller.rb b/app/controllers/templates_controller.rb index 908b9e0b27..821b8082b6 100644 --- a/app/controllers/templates_controller.rb +++ b/app/controllers/templates_controller.rb @@ -94,7 +94,7 @@ def populate_template dir = Seek::Config.append_filestore_path('source_types') if Dir.exist?(dir) - `rm #{dir}/*` + FileUtils.rm_f(Dir.glob("#{dir}/*")) else FileUtils.mkdir_p(dir) end @@ -116,7 +116,7 @@ def populate_template # post def template_attributes template = Template.find(params[:id]) - items = template.template_attributes.map { |a| { id: a.id, title: a.title } } + items = template.template_attributes.map { |a| { id: a.id, title: a.title.sanitize } } respond_to do |format| format.json { render json: items.to_json } end @@ -172,7 +172,7 @@ def set_status elsif File.exist?(resultfile) res = File.read(resultfile) @status = res - `rm #{resultfile}` + FileUtils.rm_f(resultfile) else @status = 'not_started' end @@ -187,12 +187,12 @@ def resultfile end def running! - `touch #{lockfile}` + FileUtils.touch(lockfile) set_status end def done! - `rm -f #{lockfile}` + FileUtils.rm_f(lockfile) end def running? diff --git a/app/helpers/samples_helper.rb b/app/helpers/samples_helper.rb index 5c37ad0c79..3560b35617 100644 --- a/app/helpers/samples_helper.rb +++ b/app/helpers/samples_helper.rb @@ -143,7 +143,9 @@ def display_attribute_value(value, attribute, options = {}) when Seek::Samples::BaseType::CV seek_cv_attribute_display(value, attribute) when Seek::Samples::BaseType::CV_LIST - value.each{|v| seek_cv_attribute_display(v, attribute) }.join(', ') + value.map do |v| + seek_cv_attribute_display(v, attribute) + end.join(', ').html_safe when Seek::Samples::BaseType::LINKED_EXTENDED_METADATA linked_extended_metadata_attribute_display(value, attribute) when Seek::Samples::BaseType::LINKED_EXTENDED_METADATA_MULTI @@ -171,12 +173,15 @@ def select_cv_source_ontology(sample_controlled_vocab) end def seek_cv_attribute_display(value, attribute) - term = attribute.sample_controlled_vocab.sample_controlled_vocab_terms.where(label:value).last - content = value + term = attribute.sample_controlled_vocab.sample_controlled_vocab_terms.where(label: value).last if term && term.iri.present? - content << " (#{term.iri}) " + iri_content = term.iri.match?(/^https?:\/\//) ? link_to(term.iri, term.iri, target: '_blank') : term.iri + label_tag = content_tag(:label, term.label, class: 'term-label') + iri_tag = content_tag(:label, iri_content, class: 'term-iri badge') + "#{label_tag}#{iri_tag}".html_safe + else + term.label end - content end def linked_extended_metadata_attribute_display(value, attribute) diff --git a/app/helpers/templates_helper.rb b/app/helpers/templates_helper.rb index ff3a032de4..fea648db03 100644 --- a/app/helpers/templates_helper.rb +++ b/app/helpers/templates_helper.rb @@ -17,12 +17,12 @@ def template_attribute_details(template_attribute) def load_templates privilege = Seek::Permissions::Translator.translate('view') Template.order(:group, :group_order).select { |t| t.can_perform?(privilege) }.map do |item| - { title: item.title, - group: item.group, - level: item.level, - organism: item.organism, + { title: item.title&.sanitize, + group: item.group&.sanitize, + level: item.level&.sanitize, + organism: item.organism&.sanitize, template_id: item.id, - description: item.description, + description: item.description&.sanitize, group_order: item.group_order, attributes: item.template_attributes.order(:pos).map { |a| map_template_attributes(a) } } end @@ -77,19 +77,19 @@ def template_attribute_type_link(template_attribute) def map_template_attributes(attribute) { attribute_type_id: attribute.sample_attribute_type_id, - data_type: SampleAttributeType.find(attribute.sample_attribute_type_id)&.title, + data_type: SampleAttributeType.find(attribute.sample_attribute_type_id)&.title&.sanitize, cv_id: attribute.sample_controlled_vocab_id, allow_cv_free_text: attribute.allow_cv_free_text, - title: attribute.title, + title: attribute.title&.sanitize, is_title: attribute.is_title, - short_name: attribute.short_name, - description: attribute.description, - pid: attribute.pid, + short_name: attribute.short_name&.sanitize, + description: attribute.description&.sanitize, + pid: attribute.pid&.sanitize, required: attribute.required, unit_id: attribute.unit_id, pos: attribute.pos, isa_tag_id: attribute.isa_tag_id, - isa_tag_title: attribute.isa_tag&.title, + isa_tag_title: attribute.isa_tag&.title&.sanitize, linked_sample_type_id: attribute.linked_sample_type_id, template_attribute_id: attribute.id } diff --git a/app/models/sample_type.rb b/app/models/sample_type.rb index b7ba82c750..1f9ddebf15 100644 --- a/app/models/sample_type.rb +++ b/app/models/sample_type.rb @@ -60,6 +60,30 @@ class SampleType < ApplicationRecord has_annotation_type :sample_type_tag, method_name: :tags + # Creates sample attributes from an ISA template. + # @param template [Template] The ISA template to create sample attributes from. + # @param linked_sample_type [SampleType, nil] The linked sample type, if any. + def create_sample_attributes_from_isa_template(template, linked_sample_type = nil) + self.sample_attributes = template.template_attributes.map do |temp_attr| + has_seek_samples = temp_attr.sample_attribute_type.seek_sample? || temp_attr.sample_attribute_type.seek_sample_multi? + has_linked_st = linked_sample_type && has_seek_samples + + SampleAttribute.new( + title: temp_attr.title, + description: temp_attr.description, + sample_attribute_type_id: temp_attr.sample_attribute_type_id, + required: temp_attr.required, + unit_id: temp_attr.unit_id, + is_title: temp_attr.is_title, + sample_controlled_vocab_id: temp_attr.sample_controlled_vocab_id, + linked_sample_type_id: has_linked_st ? linked_sample_type&.id : nil, + isa_tag_id: temp_attr.isa_tag_id, + allow_cv_free_text: temp_attr.allow_cv_free_text, + template_attribute_id: temp_attr.id + ) + end + end + def level isa_template&.level end diff --git a/app/views/assets/_upload.html.erb b/app/views/assets/_upload.html.erb index 17c61999ec..6257a80bcb 100644 --- a/app/views/assets/_upload.html.erb +++ b/app/views/assets/_upload.html.erb @@ -85,7 +85,11 @@ <% if batch %>
- <%= button_link_to 'Add', 'new', '#', 'data-role' => 'seek-upload-field-add-remote' %> + +
+
+ <%= button_link_to 'Add', 'new', '#', 'data-role' => 'seek-upload-field-add-remote' %>
<%= content_tag :script, existing_objects.map { |o| { text: "#{o.url.blank? ? o.original_filename : o.url} (original)", id: o.id } }.to_json.html_safe, diff --git a/app/views/extended_metadata/_single_row.html.erb b/app/views/extended_metadata/_single_row.html.erb index 38cca5fa8d..dc5f93c42c 100644 --- a/app/views/extended_metadata/_single_row.html.erb +++ b/app/views/extended_metadata/_single_row.html.erb @@ -15,7 +15,7 @@
<% attr_element_name = "#{element_name}[#{index}][#{attr.title}]" %> <%= attribute_form_element(attr, value ? value[attr.title] : nil, attr_element_name, element_class) %> - <% unless attribute.description.nil? %> + <% unless attr.description.nil? %> <%= extended_metadata_attribute_description(attr.description) %> <% end %>
diff --git a/app/views/general/_item_title.html.erb b/app/views/general/_item_title.html.erb index 9d17f3335a..1a9873edac 100644 --- a/app/views/general/_item_title.html.erb +++ b/app/views/general/_item_title.html.erb @@ -29,7 +29,7 @@ gatekeeper_status_bar = item.is_asset? && item.can_manage? && (item.is_waiting_a