diff --git a/app/assets/javascripts/controlled_vocabs.js.erb b/app/assets/javascripts/controlled_vocabs.js.erb index 4d1d0ddd1b..db2a303dd3 100644 --- a/app/assets/javascripts/controlled_vocabs.js.erb +++ b/app/assets/javascripts/controlled_vocabs.js.erb @@ -30,14 +30,20 @@ var SampleTypeControlledVocab = { initialise_rich_text_editors(".rich-text-edit"); }, + copyBlankModalForm: function() { + // first destroy any select2 elements, which won't survive being cloned. They will be initialised again when reset + $j('#cv-modal select[data-role="seek-objectsinput"]').select2('destroy'); + SampleTypeControlledVocab.blankControlledVocabModelForm=$j('#cv-modal').clone(); + }, + //resets the modal resetModalControlledVocabForm: function () { $j('#cv-modal').remove(); $j('#modal-dialogues').append(SampleTypeControlledVocab.blankControlledVocabModelForm.clone()); CVTerms.init(); + ObjectsInput.init(); SampleTypeControlledVocab.bindNewControlledVocabShowEvent(); SampleTypeControlledVocab.initialise_deferred_rich_editor_modal(); - }, //selected CV item changed diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb index 0e42d115dd..261b2d2c91 100644 --- a/app/controllers/admin_controller.rb +++ b/app/controllers/admin_controller.rb @@ -13,7 +13,7 @@ def index respond_to do |format| format.html end - end + end def update_admins # Don't let admin remove themselves or they won't be able to manage roles @@ -134,6 +134,11 @@ def update_features_enabled Seek::Config.piwik_analytics_url = params[:piwik_analytics_url] Seek::Config.piwik_analytics_tracking_notice = params[:piwik_analytics_tracking_notice] + Seek::Config.custom_analytics_snippet_enabled = string_to_boolean params[:custom_analytics_snippet_enabled] + Seek::Config.custom_analytics_snippet = params[:custom_analytics_snippet] + Seek::Config.custom_analytics_tracking_notice = params[:custom_analytics_tracking_notice] + Seek::Config.custom_analytics_name = params[:custom_analytics_name] + Seek::Config.doi_minting_enabled = string_to_boolean params[:doi_minting_enabled] Seek::Config.datacite_username = params[:datacite_username] Seek::Config.datacite_password = params[:datacite_password] 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/controllers/workflow_classes_controller.rb b/app/controllers/workflow_classes_controller.rb index 9097fd341e..2539a5617f 100644 --- a/app/controllers/workflow_classes_controller.rb +++ b/app/controllers/workflow_classes_controller.rb @@ -50,6 +50,10 @@ def edit def index @workflow_classes = WorkflowClass.order(extractor: :desc).all + respond_to do |format| + format.html + format.jsonld { render json: @workflow_classes.map(&:ro_crate_metadata), adapter: :attributes } + end end private 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 94a389f07a..3145e98e86 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/admin/features_enabled.html.erb b/app/views/admin/features_enabled.html.erb index 5f5057cbe7..db7fb3abee 100644 --- a/app/views/admin/features_enabled.html.erb +++ b/app/views/admin/features_enabled.html.erb @@ -126,7 +126,7 @@ <%= admin_checkbox_setting(:require_cookie_consent, 1, Seek::Config.require_cookie_consent, - "Add a cookie consent banner and a cookie setting page", "Show a banner asking for consent for the use of cookies. SEEK uses an essential cookie for session handling, and optional cookies for tracking if set below. + "Add a cookie consent banner and a cookie setting page", "Show a banner asking for consent for the use of cookies. SEEK uses an essential cookie for session handling, and optional cookies for tracking if set below. Some cookies might be also set by embedded content. This banner will offer 3 choices: Only necessary, Embedded content, All. Setting up this option will also add a cookie setting page accessible from the footer.") %> @@ -152,6 +152,19 @@ 'Piwik tracking notice', "Warn users about the use of analytics software.") %> + <%= admin_checkbox_setting(:custom_analytics_snippet_enabled, 1, Seek::Config.custom_analytics_snippet_enabled, + "Include a custom analytics snippet", "Adds custom snippet to the page keep track of site traffic.", + onchange: toggle_appear_javascript('custom_analytics_block')) %> + +
+ <%= admin_text_setting(:custom_analytics_name, Seek::Config.custom_analytics_name, + 'Custom analytics name', "The name of the analytics software used for tracking traffic.") %> + <%= admin_textarea_setting(:custom_analytics_snippet, Seek::Config.custom_analytics_snippet, + "Code Snippet", "A custom code snippet, taking care of site traffic.") %> + <%= admin_checkbox_setting(:custom_analytics_tracking_notice, 1, Seek::Config.custom_analytics_tracking_notice, + 'Custom tracking notice', "Warn users about the use of analytics software.") %> +
+ <%= render :partial => "admin/doi_settings" %> <%= render :partial => "admin/zenodo_settings" %> 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? %> -
  • <%= image_tag_for_key('edit', edit_isa_assay_path(item, source_assay_id: item.id, study_id: item.study.id, single_page: params[:single_page]), "Edit #{t('isa_assay')}", nil, "Edit #{t('isa_assay')}") -%>
  • + <% if item&.is_assay_stream? %> +
  • <%= image_tag_for_key('edit', edit_isa_assay_path(item, source_assay_id: item.id, study_id: item.study.id, single_page: params[:single_page], is_assay_stream: true), "Edit #{assay_word}", nil, "Edit #{assay_word}") -%>
  • + <% else %> +
  • <%= image_tag_for_key('edit', edit_isa_assay_path(item, source_assay_id: item.id, study_id: item.study.id, single_page: params[:single_page], assay_stream_id: item.assay_stream_id), "Edit #{assay_word}", nil, "Edit #{assay_word}") -%>
  • + <% end %> <% else %>
  • <%= image_tag_for_key('edit', edit_assay_path(item), "Edit #{assay_word}", nil, "Edit #{assay_word}") -%>
  • <% end %> @@ -46,8 +62,8 @@ <% if item.can_manage? -%>
  • <%= image_tag_for_key('manage', manage_assay_path(item), "Manage #{assay_word}", nil, "Manage #{assay_word}") -%>
  • - <%= render :partial => 'snapshots/new_snapshot_link', :locals => {:item => item} %> + <%= render partial: 'snapshots/new_snapshot_link', locals: {item: item} %> <% end -%> - <%= delete_icon(item, current_user) %> + <%= delete_icon(item, current_user, 'Are you sure?', assay_word) %> <% end %> diff --git a/app/views/assays/show.html.erb b/app/views/assays/show.html.erb index 6f2c697b1b..9f85fc1b39 100644 --- a/app/views/assays/show.html.erb +++ b/app/views/assays/show.html.erb @@ -47,16 +47,18 @@ <%= "Assay position" %>: <%= @assay.position %>

    -

    - <%= assay_type_text -%>: - <%= link_to_assay_type(@assay) -%> -

    - <% unless @assay.is_modelling? -%> -

    - Technology type: - <%= link_to_technology_type(@assay) -%> + <% unless @assay.is_isa_json_compliant? %> +

    + <%= assay_type_text -%>: + <%= link_to_assay_type(@assay) -%>

    - <% end -%> + <% unless @assay.is_modelling? -%> +

    + Technology type: + <%= link_to_technology_type(@assay) -%> +

    + <% end -%> + <% end %> <% if Seek::Config.organisms_enabled %> <%= list_assay_organisms("Organisms", @assay.assay_organisms, { :id => "organism" }) %> @@ -68,12 +70,34 @@ <% if Seek::Config.isa_json_compliance_enabled %>

    - <%= "Is ISA-JSON compliant" %>: + Is ISA-JSON compliant: <%= @assay.is_isa_json_compliant? %>

    <%= render partial: 'isa_studies/applied_templates', locals: { resource: @assay } -%> <% end %> + <% if @assay.is_isa_json_compliant? %> + <% if @assay.is_assay_stream? %> +

    + Child assays: +

    +

    + <% else %> +

    + <%= t('assays.assay_stream') %>: + <%= link_to @assay.assay_stream.title, @assay.assay_stream %> +

    + <% end %> + <% end %> + diff --git a/app/views/cookies/consent.html.erb b/app/views/cookies/consent.html.erb index 73b1c6d32d..09c3f507bf 100644 --- a/app/views/cookies/consent.html.erb +++ b/app/views/cookies/consent.html.erb @@ -37,9 +37,9 @@ <%= link_to t('cookies.buttons.embedding'), cookies_consent_path(allow: 'necessary,embedding'), method: :post, class: 'btn btn-default' %> <%= link_to t('cookies.buttons.all'), cookies_consent_path(allow: CookieConsent::OPTIONS.join(',')), - method: :post, class: 'btn btn-default' if (Seek::Config.google_analytics_enabled || Seek::Config.piwik_analytics_enabled) %> + method: :post, class: 'btn btn-default' if Seek::Config.analytics_enabled %> - \ No newline at end of file + diff --git a/app/views/general/_cookies_info.html.erb b/app/views/general/_cookies_info.html.erb index 8a1c72f11a..5911846be5 100644 --- a/app/views/general/_cookies_info.html.erb +++ b/app/views/general/_cookies_info.html.erb @@ -3,6 +3,7 @@ <% analytics = [] %> <% analytics << 'Google Analytics' if Seek::Config.google_analytics_enabled %> <% analytics << 'Matomo' if Seek::Config.piwik_analytics_enabled %> + <% analytics << Seek::Config.custom_analytics_name if Seek::Config.custom_analytics_snippet_enabled %> <% if analytics.any? %> <%= t('cookies.analytics', analytics: analytics.join(' and '), instance_name: Seek::Config.instance_name) %> <% end %> @@ -11,5 +12,5 @@ <% if Seek::Config.privacy_enabled %> See our <%= link_to 'Privacy Policy', privacy_home_path %> for more information. <% end %> - -

    \ No newline at end of file + +

    diff --git a/app/views/isa_assays/_form.html.erb b/app/views/isa_assays/_form.html.erb index 35d5b38d2c..cc707ccf20 100644 --- a/app/views/isa_assays/_form.html.erb +++ b/app/views/isa_assays/_form.html.erb @@ -1,35 +1,40 @@ <% assay = params[:isa_assay][:assay] if params.dig(:isa_assay, :assay) %> <% study = Study.find(params[:study_id] || assay[:study_id]) %> <% -input_sample_type_id = params[:isa_assay][:input_sample_type_id] if params.dig(:isa_assay, :input_sample_type_id) -source_assay = Assay.find(params[:source_assay_id]) if params[:source_assay_id] - -if @isa_assay.assay.new_record? - if params[:source_assay_id] - assay_position = source_assay.position + 1 - else - assay_position = 0 - end -else - assay_position = @isa_assay.assay.position -end - -# assay_position = params[:source_assay_id] ? source_assay.position + 1 : 0 -input_sample_type_id ||= - if params[:is_source] - study.sample_types.second.id + source_assay = Assay.find(params[:source_assay_id]) if params[:source_assay_id] + assay_stream_id = params[:assay_stream_id] if params[:assay_stream_id] + + if @isa_assay.assay.new_record? + if params[:is_assay_stream] + assay_position = 0 + assay_class_id = AssayClass.assay_stream.id + is_assay_stream = true + else + assay_position = params[:source_assay_id].nil? ? 1 : source_assay.position + 1 + assay_class_id = AssayClass.experimental.id + is_assay_stream = false + end else - source_assay.sample_type.id if params[:source_assay_id] + assay_position = @isa_assay.assay.position + assay_class_id = @isa_assay.assay.assay_class_id + is_assay_stream = @isa_assay.assay.is_assay_stream? end -show_extended_metadata = - if params[:is_source] - true - elsif source_assay&.position&.zero? && !@isa_assay.assay.new_record? - true # Custom metadata should be shown in edit as well if assay position is 0. - else - false - end + input_sample_type_id ||= + if is_assay_stream || source_assay&.assay_class&.is_assay_stream? + study.sample_types.second.id + else + source_assay.sample_type.id if source_assay + end + + show_extended_metadata = + if is_assay_stream + true + elsif source_assay&.position&.zero? && !@isa_assay.assay.new_record? + true # Custom metadata should be shown in edit as well if assay position is 0. + else + false + end %> <%= error_messages_for :isa_assay %> @@ -60,22 +65,25 @@ show_extended_metadata = <%= assay_fields.number_field :position, value: assay_position || study.assays.length -%> - <%= assay_fields.hidden_field :assay_class_id -%> + <%= assay_fields.hidden_field :assay_stream_id, value: assay_stream_id -%> + <%= assay_fields.hidden_field :assay_class_id, value: assay_class_id -%> - <% if User.current_user -%> - <%= render partial: 'assets/manage_specific_attributes', locals:{f:assay_fields} if show_form_manage_specific_attributes? %> - <%= assay_fields.fancy_multiselect(:sops, other_projects_checkbox: true, name: "isa_assay[assay][sop_ids]")%> - <%= assay_fields.fancy_multiselect :publications, { other_projects_checkbox: true, name: "isa_assay[assay][publication_ids]" } %> - <%= assay_fields.fancy_multiselect :documents, { other_projects_checkbox: true, name: "isa_assay[assay][document_ids]" } %> - <% end -%> + <% unless is_assay_stream %> + <% if User.current_user -%> + <%= render partial: 'assets/manage_specific_attributes', locals:{f:assay_fields} if show_form_manage_specific_attributes? %> + <%= assay_fields.fancy_multiselect(:sops, other_projects_checkbox: true, name: "isa_assay[assay][sop_ids]")%> + <%= assay_fields.fancy_multiselect :publications, { other_projects_checkbox: true, name: "isa_assay[assay][publication_ids]" } %> + <%= assay_fields.fancy_multiselect :documents, { other_projects_checkbox: true, name: "isa_assay[assay][document_ids]" } %> + <% end -%> - <%= render :partial=> "assets/discussion_links_form", :locals=>{:resource => @isa_assay.assay} -%> + <%= render partial: "assets/discussion_links_form", locals: { resource: @isa_assay.assay } -%> + <% end -%> <% end -%> <%= f.hidden_field :input_sample_type_id, value: input_sample_type_id -%> -<% if @isa_assay.sample_type %> +<% unless is_assay_stream %> <%= folding_panel("Define #{t(:sample_type)} for #{t(:assay)}") do %> <%= render partial: 'isa_studies/sample_types_form', locals: {f: f, sample_type: @isa_assay.sample_type, id_suffix: "_sample_type", isa_element: "assay", action: action} %> <% end %> diff --git a/app/views/isa_assays/edit.erb b/app/views/isa_assays/edit.erb index 6afac60429..476b164dcd 100644 --- a/app/views/isa_assays/edit.erb +++ b/app/views/isa_assays/edit.erb @@ -1,5 +1,8 @@ -

    Edit <%= t('isa_assay') %>

    - +<% if @isa_assay.assay.is_assay_stream? %> +

    Edit <%= t('assays.assay_stream') %>

    +<% else %> +

    Edit <%= t('isa_assay') %>

    +<% end %> <%= render partial: "templates/template_modal" -%>
    <%= 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 b1d7bf1ebd..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 %> @@ -80,7 +77,7 @@ ' + Seek::Config.default :custom_analytics_tracking_notice, true Seek::Config.default :bioportal_api_key,'' Seek::Config.default :project_news_enabled,false Seek::Config.default :project_news_feed_urls,'' @@ -134,7 +138,7 @@ def load_seek_config_defaults! Seek::Config.default :contact_link, '' Seek::Config.default :funding_link, '' - + #Terms and conditions page Settings.defaults[:terms_enabled]= false Seek::Config.default :terms_page, File.read(Rails.root.join('config/default_data/terms_and_conditions_example')) diff --git a/config/locales/en.yml b/config/locales/en.yml index 6630e1a976..79dbb61c11 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/config.rb b/lib/seek/config.rb index b216834f0b..ffcc4484ca 100644 --- a/lib/seek/config.rb +++ b/lib/seek/config.rb @@ -7,7 +7,7 @@ module Fallbacks def instance_admins_name_fallback instance_name end - + def instance_admins_link_fallback instance_link end @@ -529,6 +529,10 @@ def self.read_project_setting_attributes register_encrypted_setting(method) if opts && opts[:encrypt] end + def self.analytics_enabled + google_analytics_enabled || piwik_analytics_enabled || custom_analytics_snippet_enabled + end + def self.schema_org_supported? true end diff --git a/lib/seek/config_setting_attributes.yml b/lib/seek/config_setting_attributes.yml index e23642e9df..6cff135b3f 100644 --- a/lib/seek/config_setting_attributes.yml +++ b/lib/seek/config_setting_attributes.yml @@ -60,6 +60,10 @@ copasi_enabled: external_search_enabled: piwik_analytics_enabled: piwik_analytics_tracking_notice: +custom_analytics_snippet_enabled: +custom_analytics_snippet: +custom_analytics_tracking_notice: +custom_analytics_name: seek_video_link: scales: delete_asset_version_enabled: 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/functional/workflow_classes_controller_test.rb b/test/functional/workflow_classes_controller_test.rb index 9a115efc52..79519f0841 100644 --- a/test/functional/workflow_classes_controller_test.rb +++ b/test/functional/workflow_classes_controller_test.rb @@ -32,6 +32,40 @@ class WorkflowClassesControllerTest < ActionController::TestCase workflow_class_avatar_path(user_added_3, user_added_3.avatar, size: '32x32'), count: 1 end + test 'get index as json-ld' do + person = FactoryBot.create(:person) + disable_authorization_checks do + FactoryBot.create(:cwl_workflow_class) + FactoryBot.create(:galaxy_workflow_class) + FactoryBot.create(:nextflow_workflow_class) + WorkflowClass.create!(title: 'My Class', key: 'mine', contributor: person) + end + + login_as(person) + + get :index, format: :jsonld + + assert_response :success + classes = JSON.parse(response.body) + assert_equal 4, classes.length + + cwl = classes.detect { |c| c['@id'] == '#cwl' } + assert cwl + assert_equal 'Common Workflow Language', cwl['name'] + assert_equal 'ComputerLanguage', cwl['@type'] + assert_equal 'CWL', cwl['alternateName'] + assert_equal({ '@id' => 'https://w3id.org/cwl/v1.0/' }, cwl['identifier']) + assert_equal({ '@id' =>'https://www.commonwl.org/' }, cwl['url']) + + assert classes.detect { |c| c['@id'] == '#galaxy' } + assert classes.detect { |c| c['@id'] == '#nextflow' } + + user_added = classes.detect { |c| c['@id'] == '#mine' } + assert_equal 'My Class', user_added['name'] + refute user_added.key?('identifier') + refute user_added.key?('url') + end + test 'admin can edit any workflow class' do person = FactoryBot.create(:person) core_type, c1, c2 = nil diff --git a/test/integration/cookie_consent_integration_test.rb b/test/integration/cookie_consent_integration_test.rb index 1dd8608fcb..17bbc00ee0 100644 --- a/test/integration/cookie_consent_integration_test.rb +++ b/test/integration/cookie_consent_integration_test.rb @@ -45,6 +45,20 @@ class CookieConsentIntegrationTest < ActionDispatch::IntegrationTest end end + test 'cookie consent banner shown with tracking option if custom analytics enabled' do + with_config_value(:require_cookie_consent, true) do + with_config_value(:custom_analytics_snippet_enabled, true) do + get root_path + + assert_select '#cookie-banner' do + assert_select 'a[href=?]', cookies_consent_path(allow: 'necessary') + assert_select 'a[href=?]', cookies_consent_path(allow: 'necessary,embedding') + assert_select 'a[href=?]', cookies_consent_path(allow: all_options) + end + end + end + end + test 'cookie consent banner not shown if not required' do with_config_value(:require_cookie_consent, false) do get root_path @@ -138,6 +152,19 @@ class CookieConsentIntegrationTest < ActionDispatch::IntegrationTest end end + test 'custom analytics code not present if only necessary cookies allowed' do + with_config_value(:require_cookie_consent, true) do + with_config_value(:custom_analytics_snippet_enabled, true) do + post cookies_consent_path, params: { allow: 'necessary' } + + get root_path + + assert_equal ['necessary'], CookieConsent.new(cookies).options + assert_select '#custom-tracking-script', count: 0 + end + end + end + test 'matomo analytics code not present if necessary and embedded cookies allowed' do with_config_value(:require_cookie_consent, true) do with_config_value(:piwik_analytics_enabled, true) do @@ -151,6 +178,19 @@ class CookieConsentIntegrationTest < ActionDispatch::IntegrationTest end end + test 'custom analytics code not present if necessary and embedded cookies allowed' do + with_config_value(:require_cookie_consent, true) do + with_config_value(:custom_analytics_snippet_enabled, true) do + post cookies_consent_path, params: { allow: 'necessary,embedding' } + + get root_path + + assert_equal ['necessary', 'embedding'], CookieConsent.new(cookies).options + assert_select '#custom-tracking-script', count: 0 + end + end + end + test 'matomo analytics code present if only all cookies allowed' do with_config_value(:require_cookie_consent, true) do with_config_value(:piwik_analytics_enabled, true) do @@ -164,6 +204,19 @@ class CookieConsentIntegrationTest < ActionDispatch::IntegrationTest end end + test 'custom analytics code present if only all cookies allowed' do + with_config_value(:require_cookie_consent, true) do + with_config_value(:custom_analytics_snippet_enabled, true) do + post cookies_consent_path, params: { allow: all_options } + + get root_path + + assert CookieConsent.new(cookies).allow_tracking? + assert_select '#custom-tracking-script', count: 1 + end + end + end + test 'matomo analytics code present if cookie consent not required' do with_config_value(:require_cookie_consent, false) do with_config_value(:piwik_analytics_enabled, true) do @@ -179,6 +232,21 @@ class CookieConsentIntegrationTest < ActionDispatch::IntegrationTest end end + test 'custom analytics code present if cookie consent not required' do + with_config_value(:require_cookie_consent, false) do + with_config_value(:custom_analytics_snippet_enabled, true) do + post cookies_consent_path, params: { allow: 'necessary' } + + get root_path + + cookie_consent = CookieConsent.new(cookies) + assert_equal ['necessary'], cookie_consent.options + assert cookie_consent.allow_tracking? + assert_select '#custom-tracking-script', count: 1 + end + end + end + test 'can access and use cookie consent page as anonymous user' do with_config_value(:require_cookie_consent, true) do with_config_value(:google_analytics_enabled, true) do 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/config_test.rb b/test/unit/config_test.rb index fcf99c62a4..669c6f84ba 100644 --- a/test/unit/config_test.rb +++ b/test/unit/config_test.rb @@ -177,6 +177,16 @@ class ConfigTest < ActiveSupport::TestCase assert_equal 'localhost/piwik/', Seek::Config.piwik_analytics_url end + test 'custom_analytics_enabled' do + assert !Seek::Config.custom_analytics_snippet_enabled + end + test 'custom analytics name' do + assert_equal 'Custom name', Seek::Config.custom_analytics_name + end + test 'custom analytics snippet' do + assert_equal '', Seek::Config.custom_analytics_snippet + end + # homepage settings test 'project_news_enabled' do assert !Seek::Config.project_news_enabled 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