From 3753ebed1f3c15c2c34284dbd3cf8e33d39962e0 Mon Sep 17 00:00:00 2001 From: Smulligan Date: Tue, 24 Sep 2024 09:51:47 -0400 Subject: [PATCH 1/7] add new instrument work type along with related metadata fields --- app/components/thumbnail_component.rb | 1 + .../work_version_metadata_component.rb | 13 ++++++- app/controllers/catalog_controller.rb | 11 ++++++ .../dashboard/form/publish_controller.rb | 11 ++++++ .../form/work_version_details_controller.rb | 11 ++++++ .../dashboard/work_versions_controller.rb | 11 ++++++ app/models/work_version.rb | 24 ++++++++++++- app/reports/all_work_versions_report.rb | 22 ++++++++++++ lib/data_cite/metadata/work_version.rb | 1 + openapi.yml | 34 +++++++++++++++++++ solr/conf/solrconfig.xml | 11 ++++++ spec/factories/work_versions.rb | 2 ++ spec/models/work_spec.rb | 13 +++++++ spec/reports/all_work_versions_report_spec.rb | 30 +++++++++++++--- .../support/feature_helpers/dashboard_form.rb | 12 +++++++ 15 files changed, 201 insertions(+), 6 deletions(-) diff --git a/app/components/thumbnail_component.rb b/app/components/thumbnail_component.rb index 5e54e85b1..2cbd0c660 100644 --- a/app/components/thumbnail_component.rb +++ b/app/components/thumbnail_component.rb @@ -60,6 +60,7 @@ def icon_map dataset: 'analytics', dissertation: 'subject', image: 'image', + instrument: '', journal: 'subject', map_or_cartographic_material: 'map', masters_culminating_experience: 'landscape', diff --git a/app/components/work_version_metadata_component.rb b/app/components/work_version_metadata_component.rb index 0f1b464b2..b8a3e595d 100644 --- a/app/components/work_version_metadata_component.rb +++ b/app/components/work_version_metadata_component.rb @@ -23,7 +23,18 @@ class WorkVersionMetadataComponent < BaseMetadataComponent :based_near, :related_url, :source, - :deposited_at + :deposited_at, + :owner, + :manufacturer, + :model, + :instrument_type, + :measured_variable, + :available_date, + :decommission_date, + :related_identifier, + :alternative_identifier, + :instrument_resource_type, + :funding_reference ].freeze MINI_ATTRIBUTES = [ diff --git a/app/controllers/catalog_controller.rb b/app/controllers/catalog_controller.rb index f599e26e3..5e8a46aae 100644 --- a/app/controllers/catalog_controller.rb +++ b/app/controllers/catalog_controller.rb @@ -142,6 +142,17 @@ def index config.add_show_field 'language_tesim', label: 'Language' config.add_show_field 'identifier_tesim', label: 'Identifier' config.add_show_field 'based_near_tesim', label: 'Based Near' + config.add_show_field 'owner_tesim', label: 'Owner' + config.add_show_field 'manufacturer_tesim', label: 'Manufacturer' + config.add_show_field 'model_tesim', label: 'Model' + config.add_show_field 'instrument_type_tesim', label: 'Instrument type' + config.add_show_field 'measured_variable_tesim', label: 'Measured variable' + config.add_show_field 'available_date_tesim', label: 'Available date' + config.add_show_field 'decommission_date_tesim', label: 'Decommission date' + config.add_show_field 'related_identifier_tesim', label: 'Related identifier' + config.add_show_field 'alternative_identifier_tesim', label: 'Alternative identifier' + config.add_show_field 'instrument_resource_type_tesim', label: 'Instrument type' + config.add_show_field 'funding_reference_tesim', label: 'Funding reference' config.add_show_field 'related_url_tesim', label: 'Related URL' config.add_show_field 'source_tesim', label: 'Source' config.add_show_field 'version_number_isi', label: 'Version Number' diff --git a/app/controllers/dashboard/form/publish_controller.rb b/app/controllers/dashboard/form/publish_controller.rb index 78db08245..9471cb370 100644 --- a/app/controllers/dashboard/form/publish_controller.rb +++ b/app/controllers/dashboard/form/publish_controller.rb @@ -115,6 +115,17 @@ def work_version_params :published_date, :depositor_agreement, :draft_curation_requested, + :owner, + :manufacturer, + :model, + :instrument_type, + :measured_variable, + :available_date, + :decommission_date, + :related_identifier, + :alternative_identifier, + :instrument_resource_type, + :funding_reference, keyword: [], contributor: [], publisher: [], diff --git a/app/controllers/dashboard/form/work_version_details_controller.rb b/app/controllers/dashboard/form/work_version_details_controller.rb index f1bf605be..3f9427d42 100644 --- a/app/controllers/dashboard/form/work_version_details_controller.rb +++ b/app/controllers/dashboard/form/work_version_details_controller.rb @@ -64,6 +64,17 @@ def work_version_params :rights, :version_name, :published_date, + :owner, + :manufacturer, + :model, + :instrument_type, + :measured_variable, + :available_date, + :decommission_date, + :related_identifier, + :alternative_identifier, + :instrument_resource_type, + :funding_reference, keyword: [], contributor: [], publisher: [], diff --git a/app/controllers/dashboard/work_versions_controller.rb b/app/controllers/dashboard/work_versions_controller.rb index 88a75eb41..959fc67d7 100644 --- a/app/controllers/dashboard/work_versions_controller.rb +++ b/app/controllers/dashboard/work_versions_controller.rb @@ -139,6 +139,17 @@ def metadata_params :rights, :version_name, :published_date, + :owner, + :manufacturer, + :model, + :instrument_type, + :measured_variable, + :available_date, + :decommission_date, + :related_identifier, + :alternative_identifier, + :instrument_resource_type, + :funding_reference, keyword: [], contributor: [], publisher: [], diff --git a/app/models/work_version.rb b/app/models/work_version.rb index 378cc005f..1ab5d4908 100644 --- a/app/models/work_version.rb +++ b/app/models/work_version.rb @@ -31,7 +31,18 @@ class WorkVersion < ApplicationRecord identifier: [:string, array: true, default: []], based_near: [:string, array: true, default: []], related_url: [:string, array: true, default: []], - source: [:string, array: true, default: []] + source: [:string, array: true, default: []], + owner: :string, + manufacturer: :string, + model: :string, + instrument_type: :string, + measured_variable: :string, + available_date: :string, + decommission_date: :string, + related_identifier: :string, + alternative_identifier: :string, + instrument_resource_type: :string, + funding_reference: :string belongs_to :work, inverse_of: :versions @@ -227,6 +238,17 @@ def label(id) rights subtitle version_name + owner + manufacturer + model + instrument_type + measured_variable + available_date + decommission_date + related_identifier + alternative_identifier + instrument_resource_type + funding_reference ].each do |field| define_method "#{field}=" do |val| super(val.presence) diff --git a/app/reports/all_work_versions_report.rb b/app/reports/all_work_versions_report.rb index ae60d05d8..d72ddceb8 100644 --- a/app/reports/all_work_versions_report.rb +++ b/app/reports/all_work_versions_report.rb @@ -28,6 +28,17 @@ def headers based_near related_url source + owner + manufacturer + model + instrument_type + measured_variable + available_date + decommission_date + related_identifier + alternative_identifier + instrument_resource_type + funding_reference views ] end @@ -64,6 +75,17 @@ def rows wv.based_near, wv.related_url, wv.source, + wv.owner, + wv.manufacturer, + wv.model, + wv.instrument_type, + wv.measured_variable, + wv.available_date, + wv.decommission_date, + wv.related_identifier, + wv.alternative_identifier, + wv.instrument_resource_type, + wv.funding_reference, views ] diff --git a/lib/data_cite/metadata/work_version.rb b/lib/data_cite/metadata/work_version.rb index 078b190eb..620f67953 100644 --- a/lib/data_cite/metadata/work_version.rb +++ b/lib/data_cite/metadata/work_version.rb @@ -12,6 +12,7 @@ class WorkVersion < Base 'dataset' => 'Dataset', 'dissertation' => 'Text', 'image' => 'Image', + 'instrument' => 'Instrument', 'journal' => 'Text', 'map_or_cartographic_material' => 'Image', 'masters_culminating_experience' => 'Text', diff --git a/openapi.yml b/openapi.yml index e6df9a54e..60f33c758 100644 --- a/openapi.yml +++ b/openapi.yml @@ -334,6 +334,40 @@ components: type: "array" items: type: "string" + + owner: + type: "string" + example: "Classifying Independent Hybridity" + manufacturer: + type: "string" + example: "Classifying Independent Hybridity" + model: + type: "string" + example: "Classifying Independent Hybridity" + instrument_type: + type: "string" + example: "Classifying Independent Hybridity" + measured_variable: + type: "string" + example: "Classifying Independent Hybridity" + available_date: + type: "string" + example: "Classifying Independent Hybridity" + decommission_date: + type: "string" + example: "Classifying Independent Hybridity" + related_identifier: + type: "string" + example: "Classifying Independent Hybridity" + alternative_identifier: + type: "string" + example: "Classifying Independent Hybridity" + instrument_resource_type: + type: "string" + example: "Classifying Independent Hybridity" + funding_reference: + type: "string" + example: "Classifying Independent Hybridity" related_url: type: "array" items: diff --git a/solr/conf/solrconfig.xml b/solr/conf/solrconfig.xml index 0ce87a771..34fb8009c 100644 --- a/solr/conf/solrconfig.xml +++ b/solr/conf/solrconfig.xml @@ -58,6 +58,17 @@ subject_tesim subtitle_tesim title_tesim + owner_tesim + manufacturer_tesim + model_tesim + instrument_type_tesim + measured_variable_tesim + available_date_tesim + decommission_date_tesim + related_identifier_tesim + alternative_identifier_tesim + instrument_resource_type_tesim + funding_reference_tesim work_type_ss diff --git a/spec/factories/work_versions.rb b/spec/factories/work_versions.rb index d9cf2b9a2..dcc9d7dfc 100644 --- a/spec/factories/work_versions.rb +++ b/spec/factories/work_versions.rb @@ -107,6 +107,8 @@ based_near { FactoryBotHelpers.fancy_geo_location } related_url { Faker::Internet.url } source { Faker::SlackEmoji.emoji } + owner { Faker::Book.author } + model { Faker::Number.leading_zero_number(digits: 5) } end end end diff --git a/spec/models/work_spec.rb b/spec/models/work_spec.rb index 2350244ff..ec9631c38 100644 --- a/spec/models/work_spec.rb +++ b/spec/models/work_spec.rb @@ -123,6 +123,7 @@ 'dataset', 'dissertation', 'image', + 'instrument', 'journal', 'map_or_cartographic_material', 'masters_culminating_experience', @@ -280,6 +281,7 @@ dataset: 'dataset', dissertation: 'dissertation', image: 'image', + instrument: 'instrument', journal: 'journal', map_or_cartographic_material: 'map_or_cartographic_material', masters_culminating_experience: 'masters_culminating_experience', @@ -539,6 +541,17 @@ removed_at_dtsi withdrawn_at_dtsi imported_metadata_from_rmd_tesim + alternative_identifier_tesim + instrument_type_tesim + available_date_tesim + decommission_date_tesim + funding_reference_tesim + instrument_resource_type_tesim + manufacturer_tesim + measured_variable_tesim + model_tesim + owner_tesim + related_identifier_tesim ) end diff --git a/spec/reports/all_work_versions_report_spec.rb b/spec/reports/all_work_versions_report_spec.rb index a6c647449..42e89ffe1 100644 --- a/spec/reports/all_work_versions_report_spec.rb +++ b/spec/reports/all_work_versions_report_spec.rb @@ -32,6 +32,17 @@ based_near related_url source + owner + manufacturer + model + instrument_type + measured_variable + available_date + decommission_date + related_identifier + alternative_identifier + instrument_resource_type + funding_reference views ] } end @@ -98,13 +109,24 @@ expect(row[18]).to eq version.based_near expect(row[19]).to eq version.related_url expect(row[20]).to eq version.source + expect(row[21]).to eq version.owner + expect(row[22]).to eq version.manufacturer + expect(row[23]).to eq version.model + expect(row[24]).to eq version.instrument_type + expect(row[25]).to eq version.measured_variable + expect(row[26]).to eq version.available_date + expect(row[27]).to eq version.decommission_date + expect(row[28]).to eq version.related_identifier + expect(row[29]).to eq version.alternative_identifier + expect(row[30]).to eq version.instrument_resource_type + expect(row[31]).to eq version.funding_reference end # Spot check view statistics - expect(yielded_rows[0][21]).to eq 6 - expect(yielded_rows[1][21]).to eq 0 - expect(yielded_rows[2][21]).to eq 1 - expect(yielded_rows[3][21]).to eq 0 + expect(yielded_rows[0][32]).to eq 6 + expect(yielded_rows[1][32]).to eq 0 + expect(yielded_rows[2][32]).to eq 1 + expect(yielded_rows[3][32]).to eq 0 end end end diff --git a/spec/support/feature_helpers/dashboard_form.rb b/spec/support/feature_helpers/dashboard_form.rb index 53eedac3c..8f28ba9fc 100644 --- a/spec/support/feature_helpers/dashboard_form.rb +++ b/spec/support/feature_helpers/dashboard_form.rb @@ -19,6 +19,11 @@ def self.fill_in_minimal_work_details_for_data_and_code_draft(work_version_metad select Work::Types.display('dataset'), from: 'work_version_work_attributes_work_type' end + def self.fill_in_minimal_work_details_for_instrument_draft(work_version_metadata) + fill_in 'work_version_title', with: work_version_metadata[:title] + select Work::Types.display('instrument'), from: 'work_version_work_attributes_work_type' + end + def self.fill_in_common_work_details(work_version_metadata) fill_in 'work_version_description', with: work_version_metadata[:description] fill_in 'work_version_published_date', with: work_version_metadata[:published_date] @@ -45,6 +50,13 @@ def self.fill_in_data_and_code_work_details(work_version_metadata) fill_in 'work_version_source', with: work_version_metadata[:source] end + def self.fill_in_instrument_work_details(work_version_metadata) + fill_in 'work_version_description', with: work_version_metadata[:description] + fill_in 'work_version_published_date', with: work_version_metadata[:published_date] + fill_in 'work_version_owner', with: work_version_metadata[:owner] + fill_in 'work_version_model', with: work_version_metadata[:model] + end + def self.fill_in_scholarly_works_work_details(work_version_metadata) fill_in_common_work_details(work_version_metadata) fill_in 'work_version_identifier', with: work_version_metadata[:identifier] From 33c6ff3d34f0f41e7cdcf59fb4a1da9e4f042531 Mon Sep 17 00:00:00 2001 From: Smulligan Date: Tue, 24 Sep 2024 09:54:04 -0400 Subject: [PATCH 2/7] add dynamic help text for title when instrument work type is selected --- .../controllers/work_type_controller.js | 18 ++++++++++++++++++ .../form/type/_work_version_fields.html.erb | 14 ++++++++++++-- config/locales/en.yml | 1 + 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 app/javascript/controllers/work_type_controller.js diff --git a/app/javascript/controllers/work_type_controller.js b/app/javascript/controllers/work_type_controller.js new file mode 100644 index 000000000..7297db809 --- /dev/null +++ b/app/javascript/controllers/work_type_controller.js @@ -0,0 +1,18 @@ +import { Controller } from 'stimulus' + +export default class extends Controller { + static targets = ['workTypeSelect', 'helpText'] + + connect() { + this.toggleHelpText(); + this.workTypeSelectTarget.addEventListener('change', () => this.toggleHelpText()); + } + + toggleHelpText() { + if (this.workTypeSelectTarget.value === 'instrument') { + this.helpTextTarget.style.display = 'block'; + } else { + this.helpTextTarget.style.display = 'none'; + } + } +} diff --git a/app/views/dashboard/form/type/_work_version_fields.html.erb b/app/views/dashboard/form/type/_work_version_fields.html.erb index 3ce6f2e58..611b73e25 100644 --- a/app/views/dashboard/form/type/_work_version_fields.html.erb +++ b/app/views/dashboard/form/type/_work_version_fields.html.erb @@ -4,10 +4,20 @@ -
+
<%= render 'form_fields/text', form: form, attribute: :title, required: true %> <%= form.fields_for :work do |work_form| %> - <%= render 'form_fields/select', form: work_form, attribute: :work_type, options_for_select: Work::Types.options_for_select_box, disabled: local_assigns[:work_type_disabled] %> + <%= work_form.select :work_type, + Work::Types.options_for_select_box, + {}, + { class: 'form-control custom-select', + id: 'work_version_work_attributes_work_type', + data: { target: 'work-type.workTypeSelect' }, + disabled: local_assigns[:work_type_disabled] } %> <% end %> + +
diff --git a/config/locales/en.yml b/config/locales/en.yml index ac7a8cdc2..82c0b64aa 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -437,6 +437,7 @@ en: required_metadata: Required Metadata required_to_publish_metadata: Needed to Publish additional_metadata: Optional Metadata + instrument_title: Use the instrument name as the work title readme: header: 'Please note:' format: Every work must include a README file labeled as “README” (e.g., README.txt, readme.md, etc.). This README file should contain essential information about your uploaded files, such as a brief description, instructions, or any other pertinent details. If you need assistance or guidance, please refer to the From e61e7fdca934b816e20331335010bf77db949042 Mon Sep 17 00:00:00 2001 From: Smulligan Date: Tue, 24 Sep 2024 09:55:58 -0400 Subject: [PATCH 3/7] add instrument pathway --- app/components/citation_component.rb | 2 +- .../delete_resource_button_component.rb | 4 +- app/forms/work_deposit_pathway.rb | 55 +++++++ app/models/work.rb | 8 +- .../_instrument_work_version_fields.html.erb | 16 ++ spec/components/citation_component_spec.rb | 46 +++++- spec/factories/works.rb | 4 + .../dashboard/work_version_form_spec.rb | 140 ++++++++++++++++++ 8 files changed, 270 insertions(+), 5 deletions(-) create mode 100644 app/views/dashboard/form/details/_instrument_work_version_fields.html.erb diff --git a/app/components/citation_component.rb b/app/components/citation_component.rb index f29980333..7a24f0308 100644 --- a/app/components/citation_component.rb +++ b/app/components/citation_component.rb @@ -13,7 +13,7 @@ def render? end def citation_display - return unless deposit_pathway.data_and_code? + return unless deposit_pathway.data_and_code? || deposit_pathway.instrument? "#{creators_citation_display}(#{year_published}). #{work_version.title} [Data set]. Scholarsphere.#{doi_url}" end diff --git a/app/components/delete_resource_button_component.rb b/app/components/delete_resource_button_component.rb index e49358eaf..76296bbd8 100644 --- a/app/components/delete_resource_button_component.rb +++ b/app/components/delete_resource_button_component.rb @@ -57,7 +57,9 @@ def work_version? 'WorkDepositPathway::ScholarlyWorks::PublishForm', 'WorkDepositPathway::General::DetailsForm', 'WorkDepositPathway::DataAndCode::DetailsForm', - 'WorkDepositPathway::DataAndCode::PublishForm' + 'WorkDepositPathway::DataAndCode::PublishForm', + 'WorkDepositPathway::Instrument::DetailsForm', + 'WorkDepositPathway::Instrument::PublishForm' ].include?(type) end diff --git a/app/forms/work_deposit_pathway.rb b/app/forms/work_deposit_pathway.rb index 0f598dc4b..0bcdcba2f 100644 --- a/app/forms/work_deposit_pathway.rb +++ b/app/forms/work_deposit_pathway.rb @@ -10,6 +10,8 @@ def details_form ScholarlyWorks::DetailsForm.new(resource) elsif data_and_code? DataAndCode::DetailsForm.new(resource) + elsif instrument? + Instrument::DetailsForm.new(resource) else General::DetailsForm.new(resource) end @@ -20,6 +22,8 @@ def publish_form ScholarlyWorks::PublishForm.new(resource) elsif data_and_code? DataAndCode::PublishForm.new(resource) + elsif instrument? + Instrument::PublishForm.new(resource) else resource end @@ -41,6 +45,10 @@ def data_and_code? Work::Types.data_and_code.include?(work_type) end + def instrument? + Work::Types.instrument.include?(work_type) + end + private attr_reader :resource @@ -276,4 +284,51 @@ def includes_readme_file end end end + + module Instrument + class DetailsForm < DetailsFormBase + def self.form_fields + WorkVersionFormBase::COMMON_FIELDS.union( + %w{ + title + owner + identifier + manufacturer + model + instrument_type + measured_variable + available_date + decommission_date + related_identifier + alternative_identifier + instrument_resource_type + funding_reference + } + ).freeze + end + + form_fields.each do |attr_name| + delegate attr_name, to: :work_version, prefix: false + delegate "#{attr_name}=", to: :work_version, prefix: false + end + + def form_partial + 'instrument_work_version' + end + end + + class PublishForm < SimpleDelegator + def self.method_missing(method_name, *args) + WorkVersion.public_send(method_name, *args) + end + + def self.respond_to_missing?(method_name, *) + WorkVersion.respond_to?(method_name) + end + + def form_partial + 'instrument_work_version' + end + end + end end diff --git a/app/models/work.rb b/app/models/work.rb index 6d7882710..de32c34b2 100644 --- a/app/models/work.rb +++ b/app/models/work.rb @@ -61,7 +61,7 @@ class Work < ApplicationRecord module Types def self.all - general.union(scholarly_works).union(data_and_code).freeze + general.union(scholarly_works).union(data_and_code).union(instrument).freeze end def self.general @@ -102,6 +102,12 @@ def self.data_and_code ].freeze end + def self.instrument + %w[ + instrument + ].freeze + end + def self.default 'dataset' end diff --git a/app/views/dashboard/form/details/_instrument_work_version_fields.html.erb b/app/views/dashboard/form/details/_instrument_work_version_fields.html.erb new file mode 100644 index 000000000..d95678527 --- /dev/null +++ b/app/views/dashboard/form/details/_instrument_work_version_fields.html.erb @@ -0,0 +1,16 @@ +<%= render 'dashboard/form/details/common_work_version_required_fields', form: form %> + +
+ <%= render 'form_fields/text', form: form, attribute: :owner %> + <%= render 'form_fields/text', form: form, attribute: :identifier %> + <%= render 'form_fields/text', form: form, attribute: :manufacturer %> + <%= render 'form_fields/text', form: form, attribute: :model %> + <%= render 'form_fields/text', form: form, attribute: :instrument_type %> + <%= render 'form_fields/text', form: form, attribute: :measured_variable %> + <%= render 'form_fields/text', form: form, attribute: :available_date %> + <%= render 'form_fields/text', form: form, attribute: :decommission_date %> + <%= render 'form_fields/text', form: form, attribute: :related_identifier %> + <%= render 'form_fields/text', form: form, attribute: :alternative_identifier %> + <%= render 'form_fields/text', form: form, attribute: :instrument_resource_type %> + <%= render 'form_fields/text', form: form, attribute: :funding_reference %> +
diff --git a/spec/components/citation_component_spec.rb b/spec/components/citation_component_spec.rb index 50b3a1902..6f64bd7f7 100644 --- a/spec/components/citation_component_spec.rb +++ b/spec/components/citation_component_spec.rb @@ -4,11 +4,12 @@ RSpec.describe CitationComponent, type: :component do let(:component) { described_class.new(work_version, pathway) } - let(:pathway) { instance_double(WorkDepositPathway, data_and_code?: data_and_code) } + let(:pathway) { instance_double(WorkDepositPathway, data_and_code?: data_and_code, instrument?: instrument) } let(:data_and_code) { true } + let(:instrument) { false } describe 'rendering' do - context 'when not given a data and code pathway' do + context 'when not given a data and code or instrument pathway' do let(:data_and_code) { false } let(:work_version) { build :work_version, :published, work: build(:work, work_type: 'article') } let(:citation_component) { render_inline(component) } @@ -56,5 +57,46 @@ end end end + + context 'when given an instrument pathway' do + let(:work_version) { + build( + :work_version, + :published, + work: build(:work, work_type: 'instrument', doi: '10.26207/123'), + title: 'Citation Title', + published_date: '2024-02-16', + doi: '10.26207/123' + ) + } + let(:authorship1) { build :authorship, given_name: 'Alan', surname: 'Grant' } + let(:citation_component) { render_inline(component) } + let(:data_and_code) { false } + let(:instrument) { true } + + before { work_version.creators = [authorship1] } + + context 'when there is one creator' do + let(:expected_citation) { 'Grant, Alan (2024). Citation Title [Data set]. Scholarsphere. https://doi.org/10.26207/123' } + + it 'renders the citation' do + expect(citation_component.css('div.keyline')).not_to be_empty + expect(citation_component.text).to include(expected_citation) + end + end + + context 'when there are multiple creators' do + let(:expected_citation) { 'Grant, Alan; Sattler, Ellie; Malcolm, Ian (2024). Citation Title [Data set]. Scholarsphere. https://doi.org/10.26207/123' } + let(:authorship2) { build :authorship, given_name: 'Ellie', surname: 'Sattler' } + let(:authorship3) { build :authorship, given_name: 'Ian', surname: 'Malcolm' } + + before { work_version.creators << [authorship2, authorship3] } + + it 'renders the instrument citation' do + expect(citation_component.css('div.keyline')).not_to be_empty + expect(citation_component.text).to include(expected_citation) + end + end + end end end diff --git a/spec/factories/works.rb b/spec/factories/works.rb index 75efbfc43..525bcbe4b 100644 --- a/spec/factories/works.rb +++ b/spec/factories/works.rb @@ -82,6 +82,10 @@ work_type { 'article' } end + trait(:instrument) do + work_type { 'instrument' } + end + trait(:general) do work_type { 'audio' } end diff --git a/spec/features/dashboard/work_version_form_spec.rb b/spec/features/dashboard/work_version_form_spec.rb index c2f47df1a..673aeabfb 100644 --- a/spec/features/dashboard/work_version_form_spec.rb +++ b/spec/features/dashboard/work_version_form_spec.rb @@ -359,6 +359,120 @@ end end end + + context 'when selecting a work type that uses the instrument deposit pathway' do + it 'shows only the fields for instrument works' do + visit dashboard_form_work_versions_path + + FeatureHelpers::DashboardForm.fill_in_minimal_work_details_for_instrument_draft(metadata) + FeatureHelpers::DashboardForm.save_and_continue + + expect(page).not_to have_field('publisher_statement') + expect(page).not_to have_field('work_version_based_near') + expect(page).not_to have_field('work_version_source') + expect(page).not_to have_field('work_version_version_name') + expect(page).to have_field('work_version_owner') + expect(page).to have_field('work_version_model') + end + + context 'when saving as draft and exiting' do + it 'creates a new work with all fields provided' do + initial_work_count = Work.count + + visit dashboard_form_work_versions_path + + FeatureHelpers::DashboardForm.fill_in_minimal_work_details_for_instrument_draft(metadata) + FeatureHelpers::DashboardForm.save_as_draft_and_exit + + expect(Work.count).to eq(initial_work_count + 1) + + new_work = Work.last + expect(new_work.work_type).to eq 'instrument' + expect(new_work.versions.length).to eq 1 + + new_work_version = new_work.versions.last + expect(page).to have_content(metadata[:title]) + expect(new_work_version.title).to eq metadata[:title] + expect(new_work_version.version_number).to eq 1 + + expect(page).to have_current_path(resource_path(new_work_version.uuid)) + expect(SolrIndexingJob).to have_received(:perform_later).at_least(:once) + end + end + + context 'when saving and_continuing' do + it 'creates a new work with all fields provided' do + initial_work_count = Work.count + + visit dashboard_form_work_versions_path + + FeatureHelpers::DashboardForm.fill_in_minimal_work_details_for_instrument_draft(metadata) + FeatureHelpers::DashboardForm.save_and_continue + FeatureHelpers::DashboardForm.fill_in_instrument_work_details(metadata) + FeatureHelpers::DashboardForm.save_and_continue + + expect(Work.count).to eq(initial_work_count + 1) + new_work = Work.last + expect(new_work.work_type).to eq 'instrument' + expect(new_work.versions.length).to eq 1 + + new_work_version = new_work.versions.last + + expect(new_work_version.version_number).to eq 1 + expect(new_work_version.title).to eq metadata[:title] + expect(new_work_version.owner).to eq metadata[:owner] + expect(new_work_version.model).to eq metadata[:model] + + expect(page).to have_current_path(dashboard_form_contributors_path('work_version', new_work_version)) + expect(SolrIndexingJob).to have_received(:perform_later).at_least(:twice) + end + + context 'with invalid data' do + it 'does not save the data and rerenders the form with errors' do + visit dashboard_form_work_versions_path + + FeatureHelpers::DashboardForm.fill_in_minimal_work_details_for_instrument_draft(metadata) + FeatureHelpers::DashboardForm.save_and_continue + FeatureHelpers::DashboardForm.fill_in_instrument_work_details(metadata) + fill_in 'work_version_description', with: '' + FeatureHelpers::DashboardForm.save_and_continue + + new_work_version = Work.last.versions.last + + expect(page).to have_current_path(dashboard_form_work_version_details_path(new_work_version)) + expect(page).to have_content 'Description is required to publish the work' + expect(SolrIndexingJob).to have_received(:perform_later).once + + expect(new_work_version.description).to be_nil + expect(new_work_version.published_date).to be_nil + expect(new_work_version.subtitle).to be_nil + expect(new_work_version.version_name).to be_nil + expect(new_work_version.publisher).to be_empty + expect(new_work_version.subject).to be_empty + expect(new_work_version.language).to be_empty + expect(new_work_version.related_url).to be_empty + expect(new_work_version.identifier).to be_empty + expect(new_work_version.based_near).to be_empty + expect(new_work_version.source).to be_empty + end + end + end + + context 'when saving-and-continuing, then hitting cancel' do + it 'returns to the resource page' do + visit dashboard_form_work_versions_path + + FeatureHelpers::DashboardForm.fill_in_minimal_work_details_for_instrument_draft(metadata) + FeatureHelpers::DashboardForm.save_and_continue + FeatureHelpers::DashboardForm.fill_in_instrument_work_details(metadata) + FeatureHelpers::DashboardForm.save_and_continue + + FeatureHelpers::DashboardForm.cancel + + expect(page).to have_content metadata[:title] + end + end + end end describe 'The Work Details tab for an existing draft work' do @@ -876,6 +990,32 @@ expect(page).not_to have_field('work_version_work_attributes_visibility_authenticated') end end + + context 'with a work that uses the instrument works deposit pathway' do + let(:work) { create :work, :instrument, versions_count: 1 } + let(:work_version) { work.versions.first } + let(:user) { work.depositor.user } + + it 'shows only the fields for a scholarly work' do + visit dashboard_form_publish_path(work_version) + + expect(page).not_to have_field('work_version_based_near') + expect(page).not_to have_field('work_version_source') + expect(page).not_to have_field('work_version_version_name') + expect(page).not_to have_field('work_version_publisher_statement') + expect(page).to have_field('work_version_owner') + expect(page).to have_field('work_version_identifier') + expect(page).to have_field('work_version_manufacturer') + expect(page).to have_field('work_version_instrument_type') + expect(page).to have_field('work_version_measured_variable') + expect(page).to have_field('work_version_available_date') + expect(page).to have_field('work_version_decommission_date') + expect(page).to have_field('work_version_related_identifier') + expect(page).to have_field('work_version_alternative_identifier') + expect(page).to have_field('work_version_instrument_resource_type') + expect(page).to have_field('work_version_funding_reference') + end + end end describe 'Publishing a new work from end-to-end', js: true do From 4a9c08eefbdab3c04358a1685739519fe55e1c98 Mon Sep 17 00:00:00 2001 From: Smulligan Date: Wed, 25 Sep 2024 10:25:01 -0400 Subject: [PATCH 4/7] add file validations checking that there is a readme as well as a separate image file --- .rubocop.yml | 1 + app/forms/work_deposit_pathway.rb | 64 +- .../dashboard/form/files/_fields.html.erb | 9 + config/locales/en.yml | 4 + .../instrument/details_form_spec.rb | 93 +++ .../instrument/publish_form_spec.rb | 639 ++++++++++++++++++ 6 files changed, 805 insertions(+), 5 deletions(-) create mode 100644 spec/forms/work_deposit_pathways/instrument/details_form_spec.rb create mode 100644 spec/forms/work_deposit_pathways/instrument/publish_form_spec.rb diff --git a/.rubocop.yml b/.rubocop.yml index 60da7e7d6..e90e64b21 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -156,4 +156,5 @@ Style/EmptyCaseCondition: Layout/LineLength: Exclude: - 'spec/features/dashboard/work_version_form_spec.rb' + - 'spec/forms/work_deposit_pathways/instrument/publish_form_spec.rb' diff --git a/app/forms/work_deposit_pathway.rb b/app/forms/work_deposit_pathway.rb index 0bcdcba2f..fecb67431 100644 --- a/app/forms/work_deposit_pathway.rb +++ b/app/forms/work_deposit_pathway.rb @@ -317,18 +317,72 @@ def form_partial end end - class PublishForm < SimpleDelegator - def self.method_missing(method_name, *args) - WorkVersion.public_send(method_name, *args) + class PublishForm < WorkVersionFormBase + def self.form_fields + WorkVersionFormBase::COMMON_FIELDS.union( + %w{ + title + owner + identifier + manufacturer + model + instrument_type + measured_variable + available_date + decommission_date + related_identifier + alternative_identifier + instrument_resource_type + funding_reference + rights + depositor_agreement + contributor + } + ).freeze end - def self.respond_to_missing?(method_name, *) - WorkVersion.respond_to?(method_name) + form_fields.each do |attr_name| + delegate attr_name, to: :work_version, prefix: false + delegate "#{attr_name}=", to: :work_version, prefix: false end + validate :includes_readme_file_and_image, + if: :published? + def form_partial 'instrument_work_version' end + + delegate :aasm_state=, + :aasm_state, + :publish, + :file_resources, + :work_attributes=, + :creators_attributes=, + :creators, + :contributor, + :file_version_memberships, + :initial_draft?, + :aasm, + :update_column, + :draft_curation_requested=, + :set_thumbnail_selection, + to: :work_version, + prefix: false + + private + + def includes_readme_file_and_image + unless file_resources.find do |fr| + fr.file_data['metadata']['size'].positive? && + fr.file_data['metadata']['filename'] =~ /readme/i + end && file_resources.find do |fr| + (fr.file_data['metadata']['filename'] !~ /readme/i && + fr.file_data['metadata']['filename'] =~ /png|jpeg|tiff/i) + end + errors.add(:file_resources, :readme_and_image) + end + end end end end diff --git a/app/views/dashboard/form/files/_fields.html.erb b/app/views/dashboard/form/files/_fields.html.erb index 506ea7504..bf25c8d30 100644 --- a/app/views/dashboard/form/files/_fields.html.erb +++ b/app/views/dashboard/form/files/_fields.html.erb @@ -13,6 +13,15 @@
  • <%= t('dashboard.form.details.readme.zip') %>
  • +<% elsif deposit_pathway.instrument? %> +

    + <%= t('dashboard.form.details.readme.header') %> +

      +
    • <%= t('dashboard.form.details.readme.format') %><%= link_to t('dashboard.form.details.readme.documentation'), 'https://docs.scholarsphere.psu.edu/guides/writing-readme/' %>.
    • +
    • <%= t('dashboard.form.details.image.format') %>
    • +
    • <%= t('dashboard.form.details.image.zip') %>
    • +
    +

    <% end %> <% if form.object.file_version_memberships.any? %> diff --git a/config/locales/en.yml b/config/locales/en.yml index 82c0b64aa..e32b71f7e 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -137,6 +137,7 @@ en: blank: *required_to_publish file_resources: readme: must include a separate README file labeled as “README” in addition to other files + readme_and_image: must include a PNG, JPEG, or TIFF as well as a separate README file labeled as “README” admin: application_setting: heading: Application Settings @@ -443,6 +444,9 @@ en: format: Every work must include a README file labeled as “README” (e.g., README.txt, readme.md, etc.). This README file should contain essential information about your uploaded files, such as a brief description, instructions, or any other pertinent details. If you need assistance or guidance, please refer to the zip: If you are uploading a compressed file (e.g., .zip, .tar), ensure that the README file is a separate file outside the zipped files. documentation: ' ScholarSphere README template' + image: + format: Every work must include an image as either a PNG, JPEG, or TIFF file type. + zip: If you are uploading a compressed file (e.g., .zip, .tar), ensure that the README and image files are separate files outside the zipped files. contributors: edit: diff --git a/spec/forms/work_deposit_pathways/instrument/details_form_spec.rb b/spec/forms/work_deposit_pathways/instrument/details_form_spec.rb new file mode 100644 index 000000000..e6f06281e --- /dev/null +++ b/spec/forms/work_deposit_pathways/instrument/details_form_spec.rb @@ -0,0 +1,93 @@ +# frozen_string_literal: true + +require 'rails_helper' +require_relative '../_shared_examples_for_work_deposit_pathway_form' +require_relative '../_shared_examples_for_details_form' + +RSpec.describe WorkDepositPathway::Instrument::DetailsForm, type: :model do + subject(:form) { described_class.new(wv) } + + let(:wv) { + build( + :work_version, + attributes: { + 'description' => description, + 'published_date' => '2021', + 'owner' => 'test owner', + 'manufacturer' => 'test manufacturer', + 'model' => 'test model', + 'instrument_type' => 'test type', + 'measured_variable' => 'test measured variable', + 'available_date' => '2022', + 'decommission_date' => '2024', + 'related_identifier' => 'test related id', + 'alternative_identifier' => 'test alternative id', + 'instrument_resource_type' => 'test resource type', + 'funding_reference' => 'test funding ref' + } + ) + } + + let(:description) { 'test description' } + + it_behaves_like 'a work deposit pathway form' + it_behaves_like 'a work deposit pathway details form' + + describe '.form_fields' do + it "returns a frozen array of the names of the form's fields" do + expect(described_class.form_fields).to match_array %w{ + title + owner + identifier + manufacturer + model + instrument_type + measured_variable + available_date + decommission_date + related_identifier + alternative_identifier + instrument_resource_type + funding_reference + description + keyword + language + published_date + publisher + related_url + subject + subtitle + } + + expect(described_class.form_fields).to be_frozen + end + end + + describe '#form_partial' do + it 'returns instrument_work_version' do + expect(form.form_partial).to eq 'instrument_work_version' + end + end + + describe 'attribute initialization' do + it "sets the form attributes correctly from the given object's attributes" do + expect(form).to have_attributes( + { + description: 'test description', + published_date: '2021', + owner: 'test owner', + manufacturer: 'test manufacturer', + model: 'test model', + instrument_type: 'test type', + measured_variable: 'test measured variable', + available_date: '2022', + decommission_date: '2024', + related_identifier: 'test related id', + alternative_identifier: 'test alternative id', + instrument_resource_type: 'test resource type', + funding_reference: 'test funding ref' + } + ) + end + end +end diff --git a/spec/forms/work_deposit_pathways/instrument/publish_form_spec.rb b/spec/forms/work_deposit_pathways/instrument/publish_form_spec.rb new file mode 100644 index 000000000..8f1faa4ea --- /dev/null +++ b/spec/forms/work_deposit_pathways/instrument/publish_form_spec.rb @@ -0,0 +1,639 @@ +# frozen_string_literal: true + +require 'rails_helper' +require_relative '../_shared_examples_for_work_deposit_pathway_form' + +RSpec::Support::ObjectFormatter.default_instance.max_formatted_output_length = nil +RSpec.describe WorkDepositPathway::Instrument::PublishForm, type: :model do + subject(:form) { described_class.new(wv) } + + let(:wv) { + build( + :work_version, + attributes: { + 'title' => 'test title', + 'description' => description, + 'published_date' => '2021', + 'owner' => 'test owner', + 'manufacturer' => 'test manufacturer', + 'model' => 'test model', + 'instrument_type' => 'test type', + 'measured_variable' => 'test measured variable', + 'available_date' => '2022', + 'decommission_date' => '2024', + 'related_identifier' => 'test related id', + 'alternative_identifier' => 'test alternative id', + 'instrument_resource_type' => 'test resource type', + 'funding_reference' => 'test funding ref' + } + ) + } + + let(:description) { 'test description' } + + it_behaves_like 'a work deposit pathway form' + + it { is_expected.to delegate_method(:aasm_state).to(:work_version) } + it { is_expected.to delegate_method(:publish).to(:work_version) } + it { is_expected.to delegate_method(:file_resources).to(:work_version) } + it { is_expected.to delegate_method(:creators).to(:work_version) } + it { is_expected.to delegate_method(:contributor).to(:work_version) } + it { is_expected.to delegate_method(:file_version_memberships).to(:work_version) } + it { is_expected.to delegate_method(:initial_draft?).to(:work_version) } + it { is_expected.to delegate_method(:aasm).to(:work_version) } + it { is_expected.to delegate_method(:update_column).to(:work_version) } + it { is_expected.to delegate_method(:set_thumbnail_selection).to(:work_version) } + + describe '#aasm_state=' do + before { allow(wv).to receive(:aasm_state=) } + + let(:arg) { double } + + it 'delegates to the given work version' do + form.aasm_state = arg + expect(wv).to have_received(:aasm_state=).with(arg) + end + end + + describe '#work_attributes=' do + before { allow(wv).to receive(:work_attributes=) } + + let(:arg) { double } + + it 'delegates to the given work version' do + form.work_attributes = arg + expect(wv).to have_received(:work_attributes=).with(arg) + end + end + + describe '#creators_attributes=' do + before { allow(wv).to receive(:creators_attributes=) } + + let(:arg) { double } + + it 'delegates to the given work version' do + form.creators_attributes = arg + expect(wv).to have_received(:creators_attributes=).with(arg) + end + end + + describe '.form_fields' do + it "returns a frozen array of the names of the form's fields" do + expect(described_class.form_fields).to match_array %w{ + title + owner + identifier + manufacturer + model + instrument_type + measured_variable + available_date + decommission_date + related_identifier + alternative_identifier + instrument_resource_type + funding_reference + description + keyword + language + published_date + publisher + related_url + subject + subtitle + contributor + depositor_agreement + rights + } + + expect(described_class.form_fields).to be_frozen + end + end + + describe 'attribute initialization' do + it "sets the form attributes correctly from the given object's attributes" do + expect(form).to have_attributes( + { + title: 'test title', + description: 'test description', + published_date: '2021', + owner: 'test owner', + manufacturer: 'test manufacturer', + model: 'test model', + instrument_type: 'test type', + measured_variable: 'test measured variable', + available_date: '2022', + decommission_date: '2024', + related_identifier: 'test related id', + alternative_identifier: 'test alternative id', + instrument_resource_type: 'test resource type', + funding_reference: 'test funding ref' + } + ) + end + end + + describe 'validation' do + context "when the form's work version is otherwise valid for publishing" do + let(:wv) { build :work_version, :with_creators, description: 'description', published_date: '2020' } + + context "when the form's work version is published" do + before { wv.publish } + + context "when the form's work version has an image file that does not match 'readme'" do + before { wv.file_resources << build(:file_resource) } + + context "when the form's work version does not have a file with a name that matches 'readme'" do + it 'is invalid' do + expect(form).not_to be_valid + expect(form.errors[:file_resources]).to include(I18n.t('activemodel.errors.models.work_version.attributes.file_resources.readme_and_image')) + end + end + + context "when the form's work version has a file named 'README.md" do + context 'when the file is empty' do + before { wv.file_resources << build(:file_resource, :empty_readme_md) } + + it 'is invalid' do + expect(form).not_to be_valid + expect(form.errors[:file_resources]).to include(I18n.t('activemodel.errors.models.work_version.attributes.file_resources.readme_and_image')) + end + end + + context 'when the file is not empty' do + before { wv.file_resources << build(:file_resource, :readme_md) } + + it 'is valid' do + expect(form).to be_valid + end + end + end + + context "when the form's work version has a file named 'readme.txt" do + context 'when the file is empty' do + before { wv.file_resources << build(:file_resource, :empty_readme_txt) } + + it 'is invalid' do + expect(form).not_to be_valid + expect(form.errors[:file_resources]).to include(I18n.t('activemodel.errors.models.work_version.attributes.file_resources.readme_and_image')) + end + end + + context 'when the file is not empty' do + before { wv.file_resources << build(:file_resource, :readme_txt) } + + it 'is valid' do + expect(form).to be_valid + end + end + end + end + + context "when the form's work version does not have an image file" do + before { wv.file_resources << build(:file_resource, :pdf) } + + context "when the form's work version does not have a file with a name that matches 'readme'" do + it 'is invalid' do + expect(form).not_to be_valid + expect(form.errors[:file_resources]).to include(I18n.t('activemodel.errors.models.work_version.attributes.file_resources.readme_and_image')) + end + end + + context "when the form's work version has a file named 'README.md" do + context 'when the file is empty' do + before { wv.file_resources << build(:file_resource, :empty_readme_md) } + + it 'is invalid' do + expect(form).not_to be_valid + expect(form.errors[:file_resources]).to include(I18n.t('activemodel.errors.models.work_version.attributes.file_resources.readme_and_image')) + end + end + + context 'when the file is not empty' do + before { wv.file_resources << build(:file_resource, :readme_md) } + + it 'is invalid' do + expect(form).not_to be_valid + expect(form.errors[:file_resources]).to include(I18n.t('activemodel.errors.models.work_version.attributes.file_resources.readme_and_image')) + end + end + end + + context "when the form's work version has a file named 'readme.txt" do + context 'when the file is empty' do + before { wv.file_resources << build(:file_resource, :empty_readme_txt) } + + it 'is invalid' do + expect(form).not_to be_valid + expect(form.errors[:file_resources]).to include(I18n.t('activemodel.errors.models.work_version.attributes.file_resources.readme_and_image')) + end + end + + context 'when the file is not empty' do + before { wv.file_resources << build(:file_resource, :readme_txt) } + + it 'is invalid' do + expect(form).not_to be_valid + expect(form.errors[:file_resources]).to include(I18n.t('activemodel.errors.models.work_version.attributes.file_resources.readme_and_image')) + end + end + end + end + end + + context "when the form's work version is not published" do + context "when the form's work version has an image file with a name that does not match 'readme'" do + before { wv.file_resources << build(:file_resource) } + + context "when the form's work version does not have a file with a name that matches 'readme'" do + it 'is valid' do + expect(form).to be_valid + end + end + + context "when the form's work version has a file named 'README.md" do + context 'when the file is empty' do + before { wv.file_resources << build(:file_resource, :empty_readme_md) } + + it 'is valid' do + expect(form).to be_valid + end + end + + context 'when the file is not empty' do + before { wv.file_resources << build(:file_resource, :readme_md) } + + it 'is valid' do + expect(form).to be_valid + end + end + end + + context "when the form's work version has a file named 'readme.txt" do + context 'when the file is empty' do + before { wv.file_resources << build(:file_resource, :empty_readme_txt) } + + it 'is valid' do + expect(form).to be_valid + end + end + + context 'when the file is not empty' do + before { wv.file_resources << build(:file_resource, :readme_txt) } + + it 'is valid' do + expect(form).to be_valid + end + end + end + end + + context "when the form's work version does not have an image file" do + context "when the form's work version does not have a file with a name that matches 'readme'" do + it 'is valid' do + expect(form).to be_valid + end + end + + context "when the form's work version has a file named 'README.md" do + context 'when the file is empty' do + before { wv.file_resources << build(:file_resource, :empty_readme_md) } + + it 'is valid' do + expect(form).to be_valid + end + end + + context 'when the file is not empty' do + before { wv.file_resources << build(:file_resource, :readme_md) } + + it 'is valid' do + expect(form).to be_valid + end + end + end + + context "when the form's work version has a file named 'readme.txt" do + context 'when the file is empty' do + before { wv.file_resources << build(:file_resource, :empty_readme_txt) } + + it 'is valid' do + expect(form).to be_valid + end + end + + context 'when the file is not empty' do + before { wv.file_resources << build(:file_resource, :readme_txt) } + + it 'is valid' do + expect(form).to be_valid + end + end + end + end + end + end + + context "when the form's work version is not otherwise valid for publishing" do + let(:wv) { build :work_version, :with_creators, description: nil, published_date: '2020' } + + context "when the form's work version is published" do + before { wv.publish } + + context "when the form's work version has an image file with a name that does not match 'readme'" do + before { wv.file_resources << build(:file_resource) } + + context "when the form's work version does not have a file with a name that matches 'readme'" do + it 'is invalid' do + expect(form).not_to be_valid + expect(form.errors[:file_resources]).to include(I18n.t('activemodel.errors.models.work_version.attributes.file_resources.readme_and_image')) + expect(form.errors[:description]).to include(I18n.t('activerecord.errors.models.work_version.attributes.description.blank')) + end + end + + context "when the form's work version has a file named 'README.md" do + context 'when the file is empty' do + before { wv.file_resources << build(:file_resource, :empty_readme_md) } + + it 'is invalid' do + expect(form).not_to be_valid + expect(form.errors[:file_resources]).to include(I18n.t('activemodel.errors.models.work_version.attributes.file_resources.readme_and_image')) + expect(form.errors[:description]).to include(I18n.t('activerecord.errors.models.work_version.attributes.description.blank')) + end + end + + context 'when the file is not empty' do + before { wv.file_resources << build(:file_resource, :readme_md) } + + it 'is invalid' do + expect(form).not_to be_valid + expect(form.errors[:description]).to include(I18n.t('activerecord.errors.models.work_version.attributes.description.blank')) + end + end + end + + context "when the form's work version has a file named 'readme.txt" do + context 'when the file is empty' do + before { wv.file_resources << build(:file_resource, :empty_readme_txt) } + + it 'is invalid' do + expect(form).not_to be_valid + expect(form.errors[:file_resources]).to include(I18n.t('activemodel.errors.models.work_version.attributes.file_resources.readme_and_image')) + expect(form.errors[:description]).to include(I18n.t('activerecord.errors.models.work_version.attributes.description.blank')) + end + end + + context 'when the file is not empty' do + before { wv.file_resources << build(:file_resource, :readme_txt) } + + it 'is invalid' do + expect(form).not_to be_valid + expect(form.errors[:description]).to include(I18n.t('activerecord.errors.models.work_version.attributes.description.blank')) + end + end + end + end + + context "when the form's work version does not have an image file" do + context "when the form's work version does not have a file with a name that matches 'readme'" do + it 'is invalid' do + expect(form).not_to be_valid + expect(form.errors[:file_resources]).to include(I18n.t('activemodel.errors.models.work_version.attributes.file_resources.readme_and_image')) + expect(form.errors[:description]).to include(I18n.t('activerecord.errors.models.work_version.attributes.description.blank')) + end + end + + context "when the form's work version has a file named 'README.md" do + context 'when the file is empty' do + before { wv.file_resources << build(:file_resource, :empty_readme_md) } + + it 'is invalid' do + expect(form).not_to be_valid + expect(form.errors[:file_resources]).to include(I18n.t('activemodel.errors.models.work_version.attributes.file_resources.readme_and_image')) + expect(form.errors[:description]).to include(I18n.t('activerecord.errors.models.work_version.attributes.description.blank')) + end + end + + context 'when the file is not empty' do + before { wv.file_resources << build(:file_resource, :readme_md) } + + it 'is invalid' do + expect(form).not_to be_valid + expect(form.errors[:file_resources]).to include(I18n.t('activemodel.errors.models.work_version.attributes.file_resources.readme_and_image')) + expect(form.errors[:description]).to include(I18n.t('activerecord.errors.models.work_version.attributes.description.blank')) + end + end + end + + context "when the form's work version has a file named 'readme.txt" do + context 'when the file is empty' do + before { wv.file_resources << build(:file_resource, :empty_readme_txt) } + + it 'is invalid' do + expect(form).not_to be_valid + expect(form.errors[:file_resources]).to include(I18n.t('activemodel.errors.models.work_version.attributes.file_resources.readme_and_image')) + expect(form.errors[:description]).to include(I18n.t('activerecord.errors.models.work_version.attributes.description.blank')) + end + end + + context 'when the file is not empty' do + before { wv.file_resources << build(:file_resource, :readme_txt) } + + it 'is invalid' do + expect(form).not_to be_valid + expect(form.errors[:file_resources]).to include(I18n.t('activemodel.errors.models.work_version.attributes.file_resources.readme_and_image')) + expect(form.errors[:description]).to include(I18n.t('activerecord.errors.models.work_version.attributes.description.blank')) + end + end + end + end + end + + context "when the form's work version is not published" do + context "when the form's work version has aan image file with a name that does not match 'readme'" do + before { wv.file_resources << build(:file_resource) } + + context "when the form's work version does not have a file with a name that matches 'readme'" do + it 'is valid' do + expect(form).to be_valid + end + end + + context "when the form's work version has a file named 'README.md" do + context 'when the file is empty' do + before { wv.file_resources << build(:file_resource, :empty_readme_md) } + + it 'is valid' do + expect(form).to be_valid + end + end + + context 'when the file is not empty' do + before { wv.file_resources << build(:file_resource, :readme_md) } + + it 'is valid' do + expect(form).to be_valid + end + end + end + + context "when the form's work version has a file named 'readme.txt" do + context 'when the file is empty' do + before { wv.file_resources << build(:file_resource, :empty_readme_txt) } + + it 'is valid' do + expect(form).to be_valid + end + end + + context 'when the file is not empty' do + before { wv.file_resources << build(:file_resource, :readme_txt) } + + it 'is valid' do + expect(form).to be_valid + end + end + end + end + + context "when the form's work version does not have an image file" do + context "when the form's work version does not have a file with a name that matches 'readme'" do + it 'is valid' do + expect(form).to be_valid + end + end + + context "when the form's work version has a file named 'README.md" do + context 'when the file is empty' do + before { wv.file_resources << build(:file_resource, :empty_readme_md) } + + it 'is valid' do + expect(form).to be_valid + end + end + + context 'when the file is not empty' do + before { wv.file_resources << build(:file_resource, :readme_md) } + + it 'is valid' do + expect(form).to be_valid + end + end + end + + context "when the form's work version has a file named 'readme.txt" do + context 'when the file is empty' do + before { wv.file_resources << build(:file_resource, :empty_readme_txt) } + + it 'is valid' do + expect(form).to be_valid + end + end + + context 'when the file is not empty' do + before { wv.file_resources << build(:file_resource, :readme_txt) } + + it 'is valid' do + expect(form).to be_valid + end + end + end + end + end + end + end + + describe '#form_partial' do + it 'returns instrument_work_version' do + expect(form.form_partial).to eq 'instrument_work_version' + end + end + + describe '#save' do + let(:context) { double } + + before do + allow(wv).to receive(:save).with(context: context).and_return true + allow(wv).to receive(:attributes=) + end + + context "when the form's work version is valid" do + context 'when the form is valid' do + it "saves the form's work version" do + form.save(context: context) + expect(wv).to have_received(:save).with(context: context) + end + + context 'when the work version saves successfully' do + it 'returns true' do + expect(form.save(context: context)).to eq true + end + end + end + + context 'when the form is not valid' do + before { wv.publish } + + it 'returns false' do + expect(form.save(context: context)).to eq false + end + + it 'does not persist the form data' do + form.save(context: context) + expect(wv).not_to have_received(:save) + end + + it 'sets errors on the form' do + form.save(context: context) + expect(form.errors[:file_resources]).not_to be_empty + end + end + end + + context "when the form's work version is invalid" do + before do + wv.errors.add(:description, 'bad data!') + allow(wv).to receive(:valid?).and_return false + end + + context 'when the form is valid' do + it "transfers the work version's errors to the form" do + form.save(context: context) + expect(form.errors[:description]).to include 'bad data!' + end + + it 'does not persist the form data' do + form.save(context: context) + expect(wv).not_to have_received(:save) + end + + it 'returns false' do + expect(form.save(context: context)).to eq false + end + end + + context 'when the form is not valid' do + before { wv.publish } + + it 'sets errors on the form' do + form.save(context: context) + expect(form.errors[:description]).not_to be_empty + end + + it "transfers the work version's errors to the form" do + form.save(context: context) + expect(form.errors[:description]).to include 'bad data!' + end + + it 'does not persist the form data' do + form.save(context: context) + expect(wv).not_to have_received(:save) + end + + it 'returns false' do + expect(form.save(context: context)).to eq false + end + end + end + end +end From 0e140bbee240765c94b9035ad873883cc3cbf35b Mon Sep 17 00:00:00 2001 From: Smulligan Date: Wed, 25 Sep 2024 12:32:39 -0400 Subject: [PATCH 5/7] add keyword and publisher to instrument pathway where publisher is not visible but instead automatically set to Scholarsphere --- .../dashboard/form/publish_controller.rb | 1 + app/forms/work_deposit_pathway.rb | 1 + app/models/work_version.rb | 4 +++ .../_instrument_work_version_fields.html.erb | 1 + spec/factories/work_versions.rb | 15 ++++++++++ .../dashboard/work_version_form_spec.rb | 29 +++++++++++++++++++ 6 files changed, 51 insertions(+) diff --git a/app/controllers/dashboard/form/publish_controller.rb b/app/controllers/dashboard/form/publish_controller.rb index 9471cb370..7389d61c4 100644 --- a/app/controllers/dashboard/form/publish_controller.rb +++ b/app/controllers/dashboard/form/publish_controller.rb @@ -30,6 +30,7 @@ def update # WorkVersion#set_thumbnail_selection may be unreliable if the Shrine::ThumbnailJob is delayed @resource.set_thumbnail_selection @resource.indexing_source = Proc.new { nil } + @resource.set_publisher_as_scholarsphere if deposit_pathway.instrument? @resource.save @resource.publish elsif request_curation? && !@resource.draft_curation_requested diff --git a/app/forms/work_deposit_pathway.rb b/app/forms/work_deposit_pathway.rb index fecb67431..7e0561744 100644 --- a/app/forms/work_deposit_pathway.rb +++ b/app/forms/work_deposit_pathway.rb @@ -367,6 +367,7 @@ def form_partial :update_column, :draft_curation_requested=, :set_thumbnail_selection, + :set_publisher_as_scholarsphere, to: :work_version, prefix: false diff --git a/app/models/work_version.rb b/app/models/work_version.rb index 1ab5d4908..800809c0e 100644 --- a/app/models/work_version.rb +++ b/app/models/work_version.rb @@ -317,6 +317,10 @@ def set_thumbnail_selection end end + def set_publisher_as_scholarsphere + metadata['publisher'] = ['Scholarsphere'] + end + def initial_draft? version_number == 1 && (draft? || temporarily_published_draft?) diff --git a/app/views/dashboard/form/details/_instrument_work_version_fields.html.erb b/app/views/dashboard/form/details/_instrument_work_version_fields.html.erb index d95678527..85afc6526 100644 --- a/app/views/dashboard/form/details/_instrument_work_version_fields.html.erb +++ b/app/views/dashboard/form/details/_instrument_work_version_fields.html.erb @@ -1,6 +1,7 @@ <%= render 'dashboard/form/details/common_work_version_required_fields', form: form %>
    + <%= render 'form_fields/multi_text', form: form, attribute: :keyword %> <%= render 'form_fields/text', form: form, attribute: :owner %> <%= render 'form_fields/text', form: form, attribute: :identifier %> <%= render 'form_fields/text', form: form, attribute: :manufacturer %> diff --git a/spec/factories/work_versions.rb b/spec/factories/work_versions.rb index dcc9d7dfc..9c072d5c6 100644 --- a/spec/factories/work_versions.rb +++ b/spec/factories/work_versions.rb @@ -30,6 +30,10 @@ association :work, :article end + trait :instrument do + association :work, :instrument + end + trait :initial_draft do after(:build, :stub) do |work_version, evaluator| evaluator.work.versions.destroy_all @@ -78,6 +82,17 @@ published_date { Faker::Date.between(from: 2.years.ago, to: Date.today).iso8601 } end + # A draft instrument that has everything needed to pass validations and be published + trait :instrument_able_to_be_published do + instrument + draft + with_files + with_readme_file + with_creators + description { Faker::Lorem.paragraph } + published_date { Faker::Date.between(from: 2.years.ago, to: Date.today).iso8601 } + end + # A valid published work-version trait :published do able_to_be_published diff --git a/spec/features/dashboard/work_version_form_spec.rb b/spec/features/dashboard/work_version_form_spec.rb index 673aeabfb..a5857a7d9 100644 --- a/spec/features/dashboard/work_version_form_spec.rb +++ b/spec/features/dashboard/work_version_form_spec.rb @@ -1003,6 +1003,7 @@ expect(page).not_to have_field('work_version_source') expect(page).not_to have_field('work_version_version_name') expect(page).not_to have_field('work_version_publisher_statement') + expect(page).to have_field('work_version_keyword') expect(page).to have_field('work_version_owner') expect(page).to have_field('work_version_identifier') expect(page).to have_field('work_version_manufacturer') @@ -1382,4 +1383,32 @@ def mock_solr_indexing_job end end end + + describe 'automatically setting publisher as Scholarsphere', js: true do + context 'with a work that uses the instrument works deposit pathway' do + let(:user) { work_version.work.depositor.user } + let(:work_version) { create :work_version, :instrument_able_to_be_published } + + it 'sets the publisher to Scholarsphere automatically' do + visit dashboard_form_publish_path(work_version) + check 'I have read and agree to the deposit agreement.' + click_on 'Publish' + + expect(work_version.reload.publisher).to eq ['Scholarsphere'] + end + end + + context 'with a work that does not use the instrument works deposit pathway' do + let(:user) { work_version.work.depositor.user } + let(:work_version) { create :work_version, :able_to_be_published } + + it 'does not edit the publisher field' do + visit dashboard_form_publish_path(work_version) + check 'I have read and agree to the deposit agreement.' + click_on 'Publish' + + expect(work_version.reload.publisher).to eq [] + end + end + end end From 0b722a4a21d4d70c6668bde7a93c9f8a48d62c04 Mon Sep 17 00:00:00 2001 From: Smulligan Date: Wed, 25 Sep 2024 12:40:31 -0400 Subject: [PATCH 6/7] yarn lint fix --- app/javascript/controllers/work_type_controller.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/javascript/controllers/work_type_controller.js b/app/javascript/controllers/work_type_controller.js index 7297db809..4360c5aa4 100644 --- a/app/javascript/controllers/work_type_controller.js +++ b/app/javascript/controllers/work_type_controller.js @@ -3,16 +3,16 @@ import { Controller } from 'stimulus' export default class extends Controller { static targets = ['workTypeSelect', 'helpText'] - connect() { - this.toggleHelpText(); - this.workTypeSelectTarget.addEventListener('change', () => this.toggleHelpText()); + connect () { + this.toggleHelpText() + this.workTypeSelectTarget.addEventListener('change', () => this.toggleHelpText()) } - toggleHelpText() { + toggleHelpText () { if (this.workTypeSelectTarget.value === 'instrument') { - this.helpTextTarget.style.display = 'block'; + this.helpTextTarget.style.display = 'block' } else { - this.helpTextTarget.style.display = 'none'; + this.helpTextTarget.style.display = 'none' } } } From 78a9e8a8f2377ebbb6a156939851a4c70c7c0684 Mon Sep 17 00:00:00 2001 From: Smulligan Date: Thu, 26 Sep 2024 09:21:36 -0400 Subject: [PATCH 7/7] allow doi minting request for instrument pathway --- app/forms/work_deposit_pathway.rb | 3 +- .../dashboard/work_version_form_spec.rb | 15 +++++++- spec/forms/work_deposit_pathway_spec.rb | 38 +++++++++++++++++++ 3 files changed, 53 insertions(+), 3 deletions(-) diff --git a/app/forms/work_deposit_pathway.rb b/app/forms/work_deposit_pathway.rb index a60f32c4a..8cee66817 100644 --- a/app/forms/work_deposit_pathway.rb +++ b/app/forms/work_deposit_pathway.rb @@ -38,7 +38,7 @@ def allows_curation_request? end def allows_mint_doi_request? - data_and_code? && @resource.doi_blank? && DoiMintingStatus.new(@resource.work).blank? + (data_and_code? || instrument?) && @resource.doi_blank? && DoiMintingStatus.new(@resource.work).blank? end def work? @@ -372,6 +372,7 @@ def form_partial :aasm, :update_column, :draft_curation_requested=, + :mint_doi_requested=, :set_thumbnail_selection, :set_publisher_as_scholarsphere, to: :work_version, diff --git a/spec/features/dashboard/work_version_form_spec.rb b/spec/features/dashboard/work_version_form_spec.rb index 88f14d831..bdc3987f6 100644 --- a/spec/features/dashboard/work_version_form_spec.rb +++ b/spec/features/dashboard/work_version_form_spec.rb @@ -1411,11 +1411,22 @@ def mock_solr_indexing_job end end end - + describe 'minting a doi', js: true do let(:user) { work_version.work.depositor.user } - context 'with a draft eligible for doi minting' do + context 'with an instrument draft eligible for doi minting' do + let(:work) { create :work, work_type: 'instrument', versions_count: 1, has_draft: true, doi: nil } + let(:work_version) { work.versions.first } + + it 'renders a checkbox requesting a doi be minted upon publish' do + visit dashboard_form_publish_path(work_version) + + expect(page).to have_content(I18n.t('dashboard.form.publish.doi.label')) + end + end + + context 'with a data and code draft eligible for doi minting' do let(:work) { create :work, work_type: 'dataset', versions_count: 1, has_draft: true, doi: nil } let(:work_version) { work.versions.first } diff --git a/spec/forms/work_deposit_pathway_spec.rb b/spec/forms/work_deposit_pathway_spec.rb index e530539fc..6521da90d 100644 --- a/spec/forms/work_deposit_pathway_spec.rb +++ b/spec/forms/work_deposit_pathway_spec.rb @@ -314,6 +314,44 @@ end end + context 'when the given work version has an instrument type' do + %w[ + instrument + ].each do |t| + let(:type) { t } + + context 'when the associated work does not have a doi' do + let(:doi_blank) { true } + + context 'when doi minting is not already in progress' do + before do + allow_any_instance_of(DoiMintingStatus).to receive(:blank?).and_return(true) + end + + it 'returns true' do + expect(pathway.allows_mint_doi_request?).to eq true + end + end + + context 'when doi minting is already in progress' do + before do + allow_any_instance_of(DoiMintingStatus).to receive(:blank?).and_return(false) + end + + it 'returns false' do + expect(pathway.allows_mint_doi_request?).to eq false + end + end + end + + context 'when the associated work has a doi' do + it 'returns false' do + expect(pathway.allows_mint_doi_request?).to eq false + end + end + end + end + context 'when the given work version does not have a data and code type' do %w[ article