Skip to content

Commit

Permalink
Merge branch 'master' into validate_collections_exist
Browse files Browse the repository at this point in the history
  • Loading branch information
bess committed Dec 14, 2017
2 parents 79c138e + 33e2fb1 commit cf97541
Show file tree
Hide file tree
Showing 13 changed files with 318 additions and 16 deletions.
31 changes: 31 additions & 0 deletions app/actors/hyrax/actors/create_with_files_and_pass_types_actor.rb
@@ -0,0 +1,31 @@
module Hyrax
module Actors
# Creates a work and attaches files to the work,
# passes types through to the attach files job
class CreateWithFilesAndPassTypesActor < CreateWithFilesActor
def create(env)
@thumbnail = env.attributes.delete(:thumbnail)
@transcript = env.attributes.delete(:transcript)
@representative = env.attributes.delete(:representative)
super
end

##
# @return [TrueClass]nnn
def attach_files(files, env)
return true if files.blank?
attributes = env.attributes.merge(file_type_attributes)
AttachTypedFilesToWorkJob.perform_later(env.curation_concern, files, attributes.to_h.symbolize_keys)
true
end

def file_type_attributes
types = {}
types[:thumbnail] = @thumbnail if @thumbnail.present?
types[:transcript] = @transcript if @transcript.present?
types[:representative] = @representative if @representative.present?
types
end
end
end
end
44 changes: 44 additions & 0 deletions app/jobs/attach_typed_files_to_work_job.rb
@@ -0,0 +1,44 @@
# Converts UploadedFiles into FileSets and attaches them to works.
class AttachTypedFilesToWorkJob < AttachFilesToWorkJob
# @param [ActiveFedora::Base] work - the work object
# @param [Array<Hyrax::UploadedFile>] uploaded_files - an array of files to attach
def perform(work, uploaded_files, **work_attributes)
validate_files!(uploaded_files)
user = User.find_by_user_key(work.depositor) # BUG? file depositor ignored
work_permissions = work.permissions.map(&:to_hash)
metadata = visibility_attributes(work_attributes)
uploaded_files.each do |uploaded_file|
actor = Hyrax::Actors::FileSetActor.new(FileSet.create, user)
actor.create_metadata(metadata)
actor.create_content(uploaded_file)
set_filetypes(work: work, file_set: actor.file_set, filename: uploaded_file.file.file.original_filename, **work_attributes)
actor.attach_to_work(work)
actor.file_set.permissions_attributes = work_permissions
uploaded_file.update(file_set_uri: actor.file_set.uri)
end
end

##
# Sets the file types on the work.
#
# Simply returns if no opts are passed. Otherwise, sets representative,
# thumbnail, and transcript roles for the given fileset if filename matches
# the set name for that role.
#
# If the work does not support a file type, attempts to set this relation are
# ignored.
#
# @return [Boolean] true if the file types have been set sucessfully
def set_filetypes(work:, file_set:, filename:, **opts)
return true if opts.empty?

[:representative, :thumbnail, :transcript].each do |type|
work.public_send(:"#{type}_id=", file_set.id) if
work.respond_to?(:"#{type}_id=") && opts[type] == filename
end

# NOTE: the work may not be valid, in which case this save doesn't do anything
# see https://github.com/samvera/hyrax/blob/403d95cb2ada27fe366dd9e8df91240f3b46c461/app/actors/hyrax/actors/file_set_actor.rb#L81
work.save
end
end
36 changes: 36 additions & 0 deletions app/lib/tufts/import_record.rb
Expand Up @@ -11,6 +11,8 @@ module Tufts
# record = ImportRecord.new
# record.file = 'filename.png'
#
# @todo This class has gotten quite large. A refactor may be beneficial.
# rubocop:disable Metrics/ClassLength
class ImportRecord
include Tufts::Normalizer

Expand All @@ -21,6 +23,10 @@ class ImportRecord
Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_AUTHENTICATED,
Hydra::AccessControls::AccessRight::VISIBILITY_TEXT_VALUE_PRIVATE].freeze

THUMBNAIL_VALUE = 'thumbnail'.freeze
TRANSCRIPT_VALUE = 'transcript'.freeze
REPRESENTATIVE_VALUE = 'representative'.freeze

##
# @!attribute mapping [rw]
# @return [MiraXmlMapping]
Expand Down Expand Up @@ -64,6 +70,24 @@ def file
files.first
end

##
# @return [String]
def thumbnail
file_by_type(THUMBNAIL_VALUE)
end

##
# @return [String]
def transcript
file_by_type(TRANSCRIPT_VALUE)
end

##
# @return [String]
def representative
file_by_type(REPRESENTATIVE_VALUE)
end

##
# @return [Array<String>]
def files
Expand Down Expand Up @@ -145,6 +169,17 @@ class VisibilityError < ArgumentError; end

private

def file_by_type(type)
return '' if metadata.nil?

file_node =
metadata
.xpath("./tufts:filename[@type=\"#{type}\"]", mapping.namespaces)
.first

file_node.try(:content) || ''
end

def singular_properties
@singular_properties =
GenericObject
Expand All @@ -165,4 +200,5 @@ def values_for(field:)
values
end
end
# rubocop:enable Metrics/ClassLength
end
1 change: 1 addition & 0 deletions app/lib/tufts/xml_metadata_builder.rb
Expand Up @@ -30,6 +30,7 @@ def build
xml.metadata do
xml.mira_import(@mapping.namespaces) do
xml.parent << tm.to_s
xml['tufts'].visibility { xml.text object.visibility }
@mapping.map_sorted do |field|
if field.property == :id
xml[field.namespace.to_s]
Expand Down
21 changes: 15 additions & 6 deletions app/services/tufts/import_service.rb
Expand Up @@ -49,12 +49,10 @@ def initialize(import:, file: nil, files: [], object_id: nil)
#
# @return [ActiveFedora::Core]
def import_object!
object = record.build_object(id: object_id)
creator = User.find(file.user_id)
ability = ::Ability.new(creator)
attributes = { uploaded_files: file_ids, member_of_collection_ids: record.collections }

env = Hyrax::Actors::Environment.new(object, ability, attributes)
object = record.build_object(id: object_id)
creator = User.find(file.user_id)
ability = ::Ability.new(creator)
env = Hyrax::Actors::Environment.new(object, ability, attributes)

Hyrax::CurationConcern.actor.create(env) ||
raise(ImportError, "Failed to create object #{object.id}\n The actor stack returned `false`.")
Expand All @@ -72,6 +70,17 @@ class ImportError < RuntimeError; end

private

##
# @private
# @return [HashWithIndifferentAccess]
def attributes
{ uploaded_files: file_ids,
member_of_collection_ids: record.collections,
thumbnail: record.thumbnail,
transcript: record.transcript,
representative: record.representative }.with_indifferent_access
end

##
# @private
def file_ids
Expand Down
2 changes: 1 addition & 1 deletion app/views/contribute/deposit_view/_gis_poster.html.erb
Expand Up @@ -33,7 +33,7 @@
</div>

<%= f.hidden_field :description, value: 'throw away' %>
<%= f.text_field :geonames_placeholder, label: 'Place Keywords', help: 'Pick some keywords', class: 'geonames', help: 'Please select places this poster pertains to. Note: field with autocomplete with values via GeoNames API' %>
<%= f.text_field :geonames_placeholder, label: 'Place Keywords', class: 'geonames', help: 'Please select places this poster pertains to. Note: field with autocomplete with values via GeoNames API' %>

<div id="geotextarea" contenteditable="false">
<% unless @contribution.geonames.nil? %>
Expand Down
1 change: 1 addition & 0 deletions config/application.rb
Expand Up @@ -20,6 +20,7 @@ class Application < Rails::Application
config.to_prepare do
factory = Hyrax::CurationConcern.actor_factory
factory.use(Hyrax::Actors::HandleAssuranceActor)
factory.swap(Hyrax::Actors::CreateWithFilesActor, Hyrax::Actors::CreateWithFilesAndPassTypesActor)

# Hyrax's instructions don't work
# https://github.com/samvera/hyrax/blame/a992e37fba805665e1587f40870bde5cd3826b3f/app/services/hyrax/curation_concern.rb#L3-L18
Expand Down
4 changes: 4 additions & 0 deletions spec/factories/hyrax_uploaded_files.rb
Expand Up @@ -2,5 +2,9 @@
factory :hyrax_uploaded_file, class: Hyrax::UploadedFile do
user
file File.open('spec/fixtures/files/pdf-sample.pdf')

factory :second_uploaded_file do
file File.open('spec/fixtures/files/2.pdf')
end
end
end
35 changes: 35 additions & 0 deletions spec/fixtures/files/mira_xml_file_types.xml
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<OAI-PMH xmlns="http://www.openarchives.org/OAI/2.0/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.openarchives.org/OAI/2.0/
http://www.openarchives.org/OAI/2.0/OAI-PMH.xsd">
<responseDate>2017-09-01T19:20:30Z</responseDate>
<request verb="ListRecords" from="1998-01-15"
set="any:set"
metadataPrefix="mira_import">
http://example.com/OAI</request>
<ListRecords>
<record>
<metadata>
<mira_import xmlns:model="info:fedora/fedora-system:def/model#" xmlns:fcrepo4="http://fedora.info/definitions/v4/repository#" xmlns:iana="http://www.iana.org/assignments/relation/" xmlns:marcrelators="http://id.loc.gov/vocabulary/relators/" xmlns:dc="http://purl.org/dc/terms/" xmlns:fedoraresourcestatus="http://fedora.info/definitions/1/0/access/ObjState#" xmlns:scholarsphere="http://scholarsphere.psu.edu/ns#" xmlns:opaquehydra="http://opaquenamespace.org/ns/hydra/" xmlns:bibframe="http://bibframe.org/vocab/" xmlns:dc11="http://purl.org/dc/elements/1.1/" xmlns:ebucore="http://www.ebu.ch/metadata/ontologies/ebucore/ebucore#" xmlns:premis="http://www.loc.gov/premis/rdf/v1#" xmlns:mads="http://www.loc.gov/mads/rdf/v1#" xmlns:tufts="http://dl.tufts.edu/terms#" xmlns:edm="http://www.europeana.eu/schemas/edm/" xmlns:foaf="http://xmlns.com/foaf/0.1/" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
<tufts:filename type="transcript">2.pdf</tufts:filename>
<tufts:filename type="representative">pdf-sample.pdf</tufts:filename>
<tufts:displays_in>dl</tufts:displays_in>
<model:hasModel>Audio</model:hasModel>
<dc:title>Record with Representative and Transcript</dc:title>
</mira_import>
</metadata>
</record>
<record>
<metadata>
<mira_import xmlns:model="info:fedora/fedora-system:def/model#" xmlns:fcrepo4="http://fedora.info/definitions/v4/repository#" xmlns:iana="http://www.iana.org/assignments/relation/" xmlns:marcrelators="http://id.loc.gov/vocabulary/relators/" xmlns:dc="http://purl.org/dc/terms/" xmlns:fedoraresourcestatus="http://fedora.info/definitions/1/0/access/ObjState#" xmlns:scholarsphere="http://scholarsphere.psu.edu/ns#" xmlns:opaquehydra="http://opaquenamespace.org/ns/hydra/" xmlns:bibframe="http://bibframe.org/vocab/" xmlns:dc11="http://purl.org/dc/elements/1.1/" xmlns:ebucore="http://www.ebu.ch/metadata/ontologies/ebucore/ebucore#" xmlns:premis="http://www.loc.gov/premis/rdf/v1#" xmlns:mads="http://www.loc.gov/mads/rdf/v1#" xmlns:tufts="http://dl.tufts.edu/terms#" xmlns:edm="http://www.europeana.eu/schemas/edm/" xmlns:foaf="http://xmlns.com/foaf/0.1/" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
<tufts:filename>3.pdf</tufts:filename>
<tufts:filename type="thumbnail">fake.png</tufts:filename>
<tufts:displays_in>dl</tufts:displays_in>
<model:hasModel>Pdf</model:hasModel>
<dc:title>Record with Representative and Thumbnail</dc:title>
</mira_import>
</metadata>
</record>
</ListRecords>
</OAI-PMH>
78 changes: 78 additions & 0 deletions spec/lib/tufts/import_record_spec.rb
Expand Up @@ -16,6 +16,18 @@
end
end

shared_context 'with file types' do
include_context 'with metadata'

let(:doc) { Nokogiri::XML(File.open(file_fixture('mira_xml_file_types.xml')).read) }

let(:thumbnail_record) { described_class.new(metadata: thumbnail_node) }

let(:thumbnail_node) do
doc.root.xpath('//xmlns:record/xmlns:metadata/xmlns:mira_import', doc.root.namespaces)[1]
end
end

describe '#build_object' do
it 'builds a GenericObject by default' do
expect(record.build_object).to be_a GenericObject
Expand Down Expand Up @@ -142,6 +154,72 @@
end
end

describe '#transcript' do
it 'is empty by default' do
expect(record.transcript).to eq ''
end

context 'with no types' do
include_context 'with metadata'

it 'is empty' do
expect(record.transcript).to eq ''
end
end

context 'with file types' do
include_context 'with file types'

it 'has a transcript' do
expect(record.transcript).to eq '2.pdf'
end
end
end

describe '#thumbnail' do
it 'is empty by default' do
expect(record.thumbnail).to eq ''
end

context 'with no types' do
include_context 'with metadata'

it 'is empty' do
expect(record.thumbnail).to eq ''
end
end

context 'with file types' do
include_context 'with file types'

it 'has a thumbnail' do
expect(thumbnail_record.thumbnail).to eq 'fake.png'
end
end
end

describe '#representative' do
it 'is empty by default' do
expect(record.representative).to eq ''
end

context 'with no types' do
include_context 'with metadata'

it 'is empty' do
expect(record.representative).to eq ''
end
end

context 'with file types' do
include_context 'with file types'

it 'has a thumbnail' do
expect(record.representative).to eq 'pdf-sample.pdf'
end
end
end

describe '#files' do
it 'is an empty collection by default' do
expect(record.files).to be_empty
Expand Down
20 changes: 20 additions & 0 deletions spec/rails_helper.rb
Expand Up @@ -152,6 +152,26 @@
DatabaseCleaner.clean
end

##
# Use this example group when you want to perform jobs inline during testing.
#
# Limit to specific job classes with:
#
# ActiveJob::Base.queue_adapter.filter = [JobClass]
config.before(perform_enqueued: true) do
ActiveJob::Base.queue_adapter = :test
ActiveJob::Base.queue_adapter.perform_enqueued_jobs = true
ActiveJob::Base.queue_adapter.perform_enqueued_at_jobs = true
end

config.after(perform_enqueued: true) do
ActiveJob::Base.queue_adapter.enqueued_jobs = []
ActiveJob::Base.queue_adapter.performed_jobs = []

ActiveJob::Base.queue_adapter.perform_enqueued_jobs = false
ActiveJob::Base.queue_adapter.perform_enqueued_at_jobs = false
end

Shoulda::Matchers.configure do |shoulda_config|
shoulda_config.integrate do |with|
# Choose a test framework:
Expand Down

0 comments on commit cf97541

Please sign in to comment.