dor-services 5.32.1 → 6.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/dor-services.rb +11 -1
- data/lib/dor/datastreams/workflow_definition_ds.rb +0 -4
- data/lib/dor/datastreams/workflow_ds.rb +0 -11
- data/lib/dor/indexers/composite_indexer.rb +27 -0
- data/lib/dor/indexers/data_indexer.rb +14 -0
- data/lib/dor/indexers/describable_indexer.rb +58 -0
- data/lib/dor/indexers/editable_indexer.rb +23 -0
- data/lib/dor/indexers/identifiable_indexer.rb +84 -0
- data/lib/dor/indexers/processable_indexer.rb +70 -0
- data/lib/dor/indexers/releasable_indexer.rb +26 -0
- data/lib/dor/models/abstract.rb +5 -1
- data/lib/dor/models/admin_policy_object.rb +8 -0
- data/lib/dor/models/agreement.rb +4 -2
- data/lib/dor/models/collection.rb +7 -0
- data/lib/dor/models/concerns/describable.rb +0 -51
- data/lib/dor/models/concerns/editable.rb +0 -13
- data/lib/dor/models/concerns/identifiable.rb +5 -79
- data/lib/dor/models/concerns/processable.rb +0 -60
- data/lib/dor/models/concerns/publishable.rb +7 -38
- data/lib/dor/models/concerns/releaseable.rb +0 -15
- data/lib/dor/models/item.rb +9 -0
- data/lib/dor/models/set.rb +7 -0
- data/lib/dor/models/workflow_object.rb +7 -4
- data/lib/dor/services/public_xml_service.rb +5 -3
- data/lib/dor/services/thumbnail_service.rb +55 -0
- data/lib/dor/version.rb +1 -1
- data/lib/dor/workflow/document.rb +0 -7
- data/lib/dor/workflow/process.rb +1 -0
- metadata +24 -17
- data/lib/dor/workflow/graph.rb +0 -163
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cddc117f7297f0e5610b46e5ef181dba30fb0fcd57d7a4f21309cd68a5d5eee8
|
4
|
+
data.tar.gz: 203483f92135bffbb04b40be6e4673153863eb305518f87922b8a7027797abe8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 14cdd9ea0fa92d23c000de0f992209d9ceff820e143716b0c479ffde26c26c822c73f51f8d799e0ce6820f50beced8e2f0c69f0aca3266227a0341782f1a50ca
|
7
|
+
data.tar.gz: 4056a91b565637a56a126c35998ddbf1f1a8653821587d1a4ddba823a33df82b3a626a6747b0bc6bd6b8bd0cd6efb4e42c82178a698fbca300b33e9782198d37
|
data/lib/dor-services.rb
CHANGED
@@ -81,6 +81,16 @@ module Dor
|
|
81
81
|
|
82
82
|
require 'druid-tools'
|
83
83
|
|
84
|
+
autoload_under 'indexers' do
|
85
|
+
autoload :CompositeIndexer
|
86
|
+
autoload :DataIndexer
|
87
|
+
autoload :DescribableIndexer
|
88
|
+
autoload :EditableIndexer
|
89
|
+
autoload :IdentifiableIndexer
|
90
|
+
autoload :ProcessableIndexer
|
91
|
+
autoload :ReleasableIndexer
|
92
|
+
end
|
93
|
+
|
84
94
|
# datastreams
|
85
95
|
autoload_under 'datastreams' do
|
86
96
|
autoload :AdministrativeMetadataDS
|
@@ -154,12 +164,12 @@ module Dor
|
|
154
164
|
autoload :CleanupResetService
|
155
165
|
autoload :PublicDescMetadataService
|
156
166
|
autoload :PublicXmlService
|
167
|
+
autoload :ThumbnailService
|
157
168
|
end
|
158
169
|
|
159
170
|
# Workflow Classes
|
160
171
|
module Workflow
|
161
172
|
extend ActiveSupport::Autoload
|
162
|
-
autoload :Graph
|
163
173
|
autoload :Process
|
164
174
|
autoload :Document
|
165
175
|
end
|
@@ -37,10 +37,6 @@ class WorkflowDefinitionDs < ActiveFedora::OmDatastream
|
|
37
37
|
add_child_node(ng_xml.at_xpath('/workflow-def'), :process, self, attributes)
|
38
38
|
end
|
39
39
|
|
40
|
-
def graph(parent = nil)
|
41
|
-
Workflow::Graph.from_processes(repo, name, processes, parent)
|
42
|
-
end
|
43
|
-
|
44
40
|
def processes
|
45
41
|
ng_xml.xpath('/workflow-def/process').collect do |node|
|
46
42
|
Workflow::Process.new(repo, name, node)
|
@@ -49,17 +49,6 @@ module Dor
|
|
49
49
|
@workflows ||= workflow.nodeset.collect { |wf_node| Workflow::Document.new wf_node.to_xml }
|
50
50
|
end
|
51
51
|
|
52
|
-
def graph(dir = nil)
|
53
|
-
result = GraphViz.digraph(pid)
|
54
|
-
sg = result.add_graph('rank') { |g| g[:rank => 'same'] }
|
55
|
-
workflows.reject(&:nil?).each do |wf|
|
56
|
-
g = wf.graph(result)
|
57
|
-
sg.add_node(g.root.id) unless g.nil?
|
58
|
-
end
|
59
|
-
result['rankdir'] = dir || 'TB'
|
60
|
-
result
|
61
|
-
end
|
62
|
-
|
63
52
|
# Finds the first workflow that is expedited, then returns the value of its priority
|
64
53
|
#
|
65
54
|
# @return [Integer] value of the priority. Defaults to 0 if none of the workflows are expedited
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dor
|
4
|
+
class CompositeIndexer
|
5
|
+
attr_reader :indexers
|
6
|
+
def initialize(*indexers)
|
7
|
+
@indexers = indexers
|
8
|
+
end
|
9
|
+
|
10
|
+
def new(resource:)
|
11
|
+
Instance.new(indexers, resource: resource)
|
12
|
+
end
|
13
|
+
|
14
|
+
class Instance
|
15
|
+
attr_reader :indexers, :resource
|
16
|
+
def initialize(indexers, resource:)
|
17
|
+
@resource = resource
|
18
|
+
@indexers = indexers.map { |i| i.new(resource: resource) }
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [Hash] the merged solr document for all the sub-indexers
|
22
|
+
def to_solr
|
23
|
+
indexers.map(&:to_solr).inject({}, &:merge)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Dor
|
2
|
+
# Indexing provided by ActiveFedora
|
3
|
+
class DataIndexer
|
4
|
+
include ActiveFedora::Indexing
|
5
|
+
|
6
|
+
attr_reader :resource
|
7
|
+
def initialize(resource:)
|
8
|
+
@resource = resource
|
9
|
+
end
|
10
|
+
|
11
|
+
delegate :create_date, :modified_date, :state, :pid, :inner_object,
|
12
|
+
:datastreams, :relationships, to: :resource
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Dor
|
2
|
+
class DescribableIndexer
|
3
|
+
attr_reader :resource
|
4
|
+
def initialize(resource:)
|
5
|
+
@resource = resource
|
6
|
+
end
|
7
|
+
|
8
|
+
# @return [Hash] the partial solr document for describable concerns
|
9
|
+
def to_solr
|
10
|
+
add_metadata_format_to_solr_doc.merge(add_mods_to_solr_doc)
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_metadata_format_to_solr_doc
|
14
|
+
{ 'metadata_format_ssim' => 'mods' }
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_mods_to_solr_doc
|
18
|
+
solr_doc = {}
|
19
|
+
mods_sources = {
|
20
|
+
sw_title_display: %w(sw_display_title_tesim),
|
21
|
+
main_author_w_date: %w(sw_author_ssim sw_author_tesim),
|
22
|
+
sw_sort_author: %w(sw_author_sort_ssi),
|
23
|
+
sw_language_facet: %w(sw_language_ssim sw_language_tesim),
|
24
|
+
sw_genre: %w(sw_genre_ssim sw_genre_tesim),
|
25
|
+
format_main: %w(sw_format_ssim sw_format_tesim),
|
26
|
+
topic_facet: %w(sw_topic_ssim sw_topic_tesim),
|
27
|
+
era_facet: %w(sw_subject_temporal_ssim sw_subject_temporal_tesim),
|
28
|
+
geographic_facet: %w(sw_subject_geographic_ssim sw_subject_geographic_tesim),
|
29
|
+
[:term_values, :typeOfResource] => %w(mods_typeOfResource_ssim mods_typeOfResource_tesim),
|
30
|
+
pub_year_sort_str: %w(sw_pub_date_sort_ssi),
|
31
|
+
pub_year_int: %w(sw_pub_date_sort_isi),
|
32
|
+
pub_year_display_str: %w(sw_pub_date_facet_ssi)
|
33
|
+
}
|
34
|
+
|
35
|
+
mods_sources.each_pair do |meth, solr_keys|
|
36
|
+
vals = meth.is_a?(Array) ? resource.stanford_mods.send(meth.shift, *meth) : resource.stanford_mods.send(meth)
|
37
|
+
|
38
|
+
next if vals.nil? || (vals.respond_to?(:empty?) && vals.empty?)
|
39
|
+
|
40
|
+
solr_keys.each do |key|
|
41
|
+
solr_doc[key] ||= []
|
42
|
+
solr_doc[key].push(*vals)
|
43
|
+
end
|
44
|
+
# asterisk to avoid multi-dimensional array: push values, not the array
|
45
|
+
end
|
46
|
+
|
47
|
+
# convert multivalued fields to single value
|
48
|
+
%w(sw_pub_date_sort_ssi sw_pub_date_sort_isi sw_pub_date_facet_ssi).each do |key|
|
49
|
+
solr_doc[key] = solr_doc[key].first unless solr_doc[key].nil?
|
50
|
+
end
|
51
|
+
# some fields get explicit "(none)" placeholder values, mostly for faceting
|
52
|
+
%w(sw_language_tesim sw_genre_tesim sw_format_tesim).each do |key|
|
53
|
+
solr_doc[key] = ['(none)'] if solr_doc[key].nil? || solr_doc[key].empty?
|
54
|
+
end
|
55
|
+
solr_doc
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Dor
|
2
|
+
class EditableIndexer
|
3
|
+
include SolrDocHelper
|
4
|
+
|
5
|
+
attr_reader :resource
|
6
|
+
def initialize(resource:)
|
7
|
+
@resource = resource
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_solr
|
11
|
+
{}.tap do |solr_doc|
|
12
|
+
add_solr_value(solr_doc, 'default_rights', default_rights_for_indexing, :string, [:symbol])
|
13
|
+
add_solr_value(solr_doc, 'agreement', resource.agreement, :string, [:symbol]) if resource.agreement_object
|
14
|
+
add_solr_value(solr_doc, 'default_use_license_machine', resource.use_license, :string, [:stored_sortable])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [String] A description of the rights defined in the default object rights datastream. Can be 'Stanford', 'World', 'Dark' or 'None'
|
19
|
+
def default_rights_for_indexing
|
20
|
+
RightsMetadataDS::RIGHTS_TYPE_CODES.fetch(resource.default_rights, 'Unrecognized default rights value')
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Dor
|
2
|
+
class IdentifiableIndexer
|
3
|
+
include SolrDocHelper
|
4
|
+
|
5
|
+
attr_reader :resource
|
6
|
+
def initialize(resource:)
|
7
|
+
@resource = resource
|
8
|
+
end
|
9
|
+
|
10
|
+
## Module-level variables, shared between ALL mixin includers (and ALL *their* includers/extenders)!
|
11
|
+
## used for caching found values
|
12
|
+
@@collection_hash = {}
|
13
|
+
@@apo_hash = {}
|
14
|
+
|
15
|
+
# @return [Hash] the partial solr document for identifiable concerns
|
16
|
+
def to_solr
|
17
|
+
solr_doc = {}
|
18
|
+
resource.assert_content_model
|
19
|
+
|
20
|
+
solr_doc[Dor::INDEX_VERSION_FIELD] = Dor::VERSION
|
21
|
+
solr_doc['indexed_at_dtsi'] = Time.now.utc.xmlschema
|
22
|
+
resource.datastreams.values.each do |ds|
|
23
|
+
add_solr_value(solr_doc, 'ds_specs', ds.datastream_spec_string, :string, [:symbol]) unless ds.new?
|
24
|
+
end
|
25
|
+
|
26
|
+
add_solr_value(solr_doc, 'title_sort', resource.label, :string, [:stored_sortable])
|
27
|
+
|
28
|
+
rels_doc = Nokogiri::XML(resource.datastreams['RELS-EXT'].content)
|
29
|
+
ns_hash = {'hydra' => 'http://projecthydra.org/ns/relations#', 'fedora' => 'info:fedora/fedora-system:def/relations-external#', 'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'}
|
30
|
+
apos = rels_doc.search('//rdf:RDF/rdf:Description/hydra:isGovernedBy', ns_hash)
|
31
|
+
collections = rels_doc.search('//rdf:RDF/rdf:Description/fedora:isMemberOfCollection', ns_hash)
|
32
|
+
solrize_related_obj_titles(solr_doc, apos, @@apo_hash, 'apo_title', 'nonhydrus_apo_title', 'hydrus_apo_title')
|
33
|
+
solrize_related_obj_titles(solr_doc, collections, @@collection_hash, 'collection_title', 'nonhydrus_collection_title', 'hydrus_collection_title')
|
34
|
+
solr_doc['public_dc_relation_tesim'] ||= solr_doc['collection_title_tesim'] if solr_doc['collection_title_tesim']
|
35
|
+
solr_doc['metadata_source_ssi'] = identity_metadata_source
|
36
|
+
solr_doc
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [String] calculated value for Solr index
|
40
|
+
def identity_metadata_source
|
41
|
+
if resource.identityMetadata.otherId('catkey').first ||
|
42
|
+
resource.identityMetadata.otherId('barcode').first
|
43
|
+
'Symphony'
|
44
|
+
else
|
45
|
+
'DOR'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def solrize_related_obj_titles(solr_doc, relationships, title_hash, union_field_name, nonhydrus_field_name, hydrus_field_name)
|
52
|
+
# TODO: if you wanted to get a little fancier, you could also solrize a 2 level hierarchy and display using hierarchial facets, like
|
53
|
+
# ["SOURCE", "SOURCE : TITLE"] (e.g. ["Hydrus", "Hydrus : Special Collections"], see (exploded) tags in IdentityMetadataDS#to_solr).
|
54
|
+
title_type = :symbol # we'll get an _ssim because of the type
|
55
|
+
title_attrs = [:stored_searchable] # we'll also get a _tesim from this attr
|
56
|
+
relationships.each do |rel_node|
|
57
|
+
rel_druid = rel_node['rdf:resource']
|
58
|
+
next unless rel_druid # TODO: warning here would also be useful
|
59
|
+
rel_druid = rel_druid.gsub('info:fedora/', '')
|
60
|
+
|
61
|
+
# populate cache if necessary
|
62
|
+
unless title_hash.key?(rel_druid)
|
63
|
+
begin
|
64
|
+
related_obj = Dor.find(rel_druid)
|
65
|
+
related_obj_title = get_related_obj_display_title(related_obj, rel_druid)
|
66
|
+
is_from_hydrus = (related_obj && related_obj.tags.include?('Project : Hydrus'))
|
67
|
+
title_hash[rel_druid] = {'related_obj_title' => related_obj_title, 'is_from_hydrus' => is_from_hydrus}
|
68
|
+
rescue ActiveFedora::ObjectNotFoundError
|
69
|
+
# This may happen if the given APO or Collection does not exist (bad data)
|
70
|
+
title_hash[rel_druid] = {'related_obj_title' => rel_druid, 'is_from_hydrus' => false}
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# cache should definitely be populated, so just use that to write solr field
|
75
|
+
if title_hash[rel_druid]['is_from_hydrus']
|
76
|
+
add_solr_value(solr_doc, hydrus_field_name, title_hash[rel_druid]['related_obj_title'], title_type, title_attrs)
|
77
|
+
else
|
78
|
+
add_solr_value(solr_doc, nonhydrus_field_name, title_hash[rel_druid]['related_obj_title'], title_type, title_attrs)
|
79
|
+
end
|
80
|
+
add_solr_value(solr_doc, union_field_name, title_hash[rel_druid]['related_obj_title'], title_type, title_attrs)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Dor
|
2
|
+
class ProcessableIndexer
|
3
|
+
include SolrDocHelper
|
4
|
+
|
5
|
+
attr_reader :resource
|
6
|
+
def initialize(resource:)
|
7
|
+
@resource = resource
|
8
|
+
end
|
9
|
+
|
10
|
+
# @return [Hash] the partial solr document for processable concerns
|
11
|
+
def to_solr
|
12
|
+
solr_doc = {}
|
13
|
+
sortable_milestones = {}
|
14
|
+
current_version = '1'
|
15
|
+
begin
|
16
|
+
current_version = resource.versionMetadata.current_version_id
|
17
|
+
rescue StandardError
|
18
|
+
end
|
19
|
+
current_version_num = current_version.to_i
|
20
|
+
|
21
|
+
if resource.respond_to?('versionMetadata')
|
22
|
+
# add an entry with version id, tag and description for each version
|
23
|
+
while current_version_num > 0
|
24
|
+
new_val = "#{current_version_num};#{resource.versionMetadata.tag_for_version(current_version_num.to_s)};#{resource.versionMetadata.description_for_version(current_version_num.to_s)}"
|
25
|
+
add_solr_value(solr_doc, 'versions', new_val, :string, [:displayable])
|
26
|
+
current_version_num -= 1
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
resource.milestones.each do |milestone|
|
31
|
+
timestamp = milestone[:at].utc.xmlschema
|
32
|
+
sortable_milestones[milestone[:milestone]] ||= []
|
33
|
+
sortable_milestones[milestone[:milestone]] << timestamp
|
34
|
+
milestone[:version] ||= current_version
|
35
|
+
solr_doc['lifecycle_ssim'] ||= []
|
36
|
+
solr_doc['lifecycle_ssim'] << milestone[:milestone]
|
37
|
+
add_solr_value(solr_doc, 'lifecycle', "#{milestone[:milestone]}:#{timestamp};#{milestone[:version]}", :symbol)
|
38
|
+
end
|
39
|
+
|
40
|
+
sortable_milestones.each do |milestone, unordered_dates|
|
41
|
+
dates = unordered_dates.sort
|
42
|
+
# create the published_dttsi and published_day fields and the like
|
43
|
+
dates.each do |date|
|
44
|
+
solr_doc["#{milestone}_dttsim"] ||= []
|
45
|
+
solr_doc["#{milestone}_dttsim"] << date unless solr_doc["#{milestone}_dttsim"].include?(date)
|
46
|
+
end
|
47
|
+
# fields for OAI havester to sort on: _dttsi is trie date +stored +indexed (single valued, i.e. sortable)
|
48
|
+
solr_doc["#{milestone}_earliest_dttsi"] = dates.first
|
49
|
+
solr_doc["#{milestone}_latest_dttsi"] = dates.last
|
50
|
+
end
|
51
|
+
solr_doc['status_ssi'] = resource.status # status is singular (i.e. the current one)
|
52
|
+
solr_doc['current_version_isi'] = current_version.to_i
|
53
|
+
solr_doc['modified_latest_dttsi'] = resource.modified_date.to_datetime.utc.strftime('%FT%TZ')
|
54
|
+
add_solr_value(solr_doc, 'rights', resource.rights, :string, [:symbol]) if resource.respond_to? :rights
|
55
|
+
|
56
|
+
status_info_hash = resource.status_info
|
57
|
+
status_code = status_info_hash[:status_code]
|
58
|
+
add_solr_value(solr_doc, 'processing_status_text', simplified_status_code_disp_txt(status_code), :string, [:stored_sortable])
|
59
|
+
solr_doc['processing_status_code_isi'] = status_code # no _isi in Solrizer's default descriptors
|
60
|
+
|
61
|
+
solr_doc
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [String] text translation of the status code, minus any trailing parenthetical explanation
|
65
|
+
# e.g. 'In accessioning (described)' and 'In accessioning (described, published)' both return 'In accessioning'
|
66
|
+
def simplified_status_code_disp_txt(status_code)
|
67
|
+
Processable::STATUS_CODE_DISP_TXT[status_code].gsub(/\(.*\)$/, '').strip
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Dor
|
2
|
+
class ReleasableIndexer
|
3
|
+
include SolrDocHelper
|
4
|
+
|
5
|
+
attr_reader :resource
|
6
|
+
def initialize(resource:)
|
7
|
+
@resource = resource
|
8
|
+
end
|
9
|
+
|
10
|
+
# @return [Hash] the partial solr document for releasable concerns
|
11
|
+
def to_solr
|
12
|
+
solr_doc = {}
|
13
|
+
|
14
|
+
# TODO: sort of worried about the performance impact in bulk reindex
|
15
|
+
# situations, since released_for recurses all parent collections. jmartin 2015-07-14
|
16
|
+
resource.released_for(true).each { |release_target, release_info|
|
17
|
+
add_solr_value(solr_doc, 'released_to', release_target, :symbol, []) if release_info['release']
|
18
|
+
}
|
19
|
+
|
20
|
+
# TODO: need to solrize whether item is released to purl? does released_for return that?
|
21
|
+
# logic is: "True when there is a published lifecycle and Access Rights is anything but Dark"
|
22
|
+
|
23
|
+
solr_doc
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/dor/models/abstract.rb
CHANGED
@@ -7,5 +7,13 @@ module Dor
|
|
7
7
|
has_metadata :name => 'administrativeMetadata', :type => Dor::AdministrativeMetadataDS, :label => 'Administrative Metadata'
|
8
8
|
has_metadata :name => 'roleMetadata', :type => Dor::RoleMetadataDS, :label => 'Role Metadata'
|
9
9
|
has_metadata :name => 'defaultObjectRights', :type => Dor::DefaultObjectRightsDS, :label => 'Default Object Rights'
|
10
|
+
|
11
|
+
self.resource_indexer = CompositeIndexer.new(
|
12
|
+
DataIndexer,
|
13
|
+
DescribableIndexer,
|
14
|
+
EditableIndexer,
|
15
|
+
IdentifiableIndexer,
|
16
|
+
ProcessableIndexer
|
17
|
+
)
|
10
18
|
end
|
11
19
|
end
|
data/lib/dor/models/agreement.rb
CHANGED
@@ -64,57 +64,6 @@ module Dor
|
|
64
64
|
PublicDescMetadataService.new(self).to_xml(**options)
|
65
65
|
end
|
66
66
|
|
67
|
-
def to_solr(solr_doc = {}, *args)
|
68
|
-
solr_doc = super solr_doc, *args
|
69
|
-
add_metadata_format_to_solr_doc(solr_doc)
|
70
|
-
add_mods_to_solr_doc(solr_doc)
|
71
|
-
end
|
72
|
-
|
73
|
-
def add_metadata_format_to_solr_doc(solr_doc)
|
74
|
-
solr_doc['metadata_format_ssim'] ||= []
|
75
|
-
solr_doc['metadata_format_ssim'] += ['mods']
|
76
|
-
end
|
77
|
-
|
78
|
-
def add_mods_to_solr_doc(solr_doc)
|
79
|
-
mods_sources = {
|
80
|
-
sw_title_display: %w(sw_display_title_tesim),
|
81
|
-
main_author_w_date: %w(sw_author_ssim sw_author_tesim),
|
82
|
-
sw_sort_author: %w(sw_author_sort_ssi),
|
83
|
-
sw_language_facet: %w(sw_language_ssim sw_language_tesim),
|
84
|
-
sw_genre: %w(sw_genre_ssim sw_genre_tesim),
|
85
|
-
format_main: %w(sw_format_ssim sw_format_tesim),
|
86
|
-
topic_facet: %w(sw_topic_ssim sw_topic_tesim),
|
87
|
-
era_facet: %w(sw_subject_temporal_ssim sw_subject_temporal_tesim),
|
88
|
-
geographic_facet: %w(sw_subject_geographic_ssim sw_subject_geographic_tesim),
|
89
|
-
[:term_values, :typeOfResource] => %w(mods_typeOfResource_ssim mods_typeOfResource_tesim),
|
90
|
-
pub_year_sort_str: %w(sw_pub_date_sort_ssi),
|
91
|
-
pub_year_int: %w(sw_pub_date_sort_isi),
|
92
|
-
pub_year_display_str: %w(sw_pub_date_facet_ssi)
|
93
|
-
}
|
94
|
-
|
95
|
-
mods_sources.each_pair do |meth, solr_keys|
|
96
|
-
vals = meth.is_a?(Array) ? stanford_mods.send(meth.shift, *meth) : stanford_mods.send(meth)
|
97
|
-
|
98
|
-
next if vals.nil? || (vals.respond_to?(:empty?) && vals.empty?)
|
99
|
-
|
100
|
-
solr_keys.each do |key|
|
101
|
-
solr_doc[key] ||= []
|
102
|
-
solr_doc[key].push *vals
|
103
|
-
end
|
104
|
-
# asterisk to avoid multi-dimensional array: push values, not the array
|
105
|
-
end
|
106
|
-
|
107
|
-
# convert multivalued fields to single value
|
108
|
-
%w(sw_pub_date_sort_ssi sw_pub_date_sort_isi sw_pub_date_facet_ssi).each do |key|
|
109
|
-
solr_doc[key] = solr_doc[key].first unless solr_doc[key].nil?
|
110
|
-
end
|
111
|
-
# some fields get explicit "(none)" placeholder values, mostly for faceting
|
112
|
-
%w(sw_language_tesim sw_genre_tesim sw_format_tesim).each do |key|
|
113
|
-
solr_doc[key] = ['(none)'] if solr_doc[key].nil? || solr_doc[key].empty?
|
114
|
-
end
|
115
|
-
solr_doc
|
116
|
-
end
|
117
|
-
|
118
67
|
# @param [Boolean] force Overwrite existing XML
|
119
68
|
# @return [String] descMetadata.content XML
|
120
69
|
def set_desc_metadata_using_label(force = false)
|
@@ -45,14 +45,6 @@ module Dor
|
|
45
45
|
:uri => 'http://opendatacommons.org/licenses/odbl/1.0/' }
|
46
46
|
}.freeze
|
47
47
|
|
48
|
-
def to_solr(solr_doc = {}, *args)
|
49
|
-
solr_doc = super(solr_doc, *args)
|
50
|
-
add_solr_value(solr_doc, 'default_rights', default_rights_for_indexing, :string, [:symbol])
|
51
|
-
add_solr_value(solr_doc, 'agreement', agreement, :string, [:symbol]) if agreement_object
|
52
|
-
add_solr_value(solr_doc, 'default_use_license_machine', use_license, :string, [:stored_sortable])
|
53
|
-
solr_doc
|
54
|
-
end
|
55
|
-
|
56
48
|
# Adds a person or group to a role in the APO role metadata datastream
|
57
49
|
#
|
58
50
|
# @param role [String] the role the group or person will be filed under, ex. dor-apo-manager
|
@@ -280,11 +272,6 @@ module Dor
|
|
280
272
|
end
|
281
273
|
end
|
282
274
|
|
283
|
-
# @return [String] A description of the rights defined in the default object rights datastream. Can be 'Stanford', 'World', 'Dark' or 'None'
|
284
|
-
def default_rights_for_indexing
|
285
|
-
RightsMetadataDS::RIGHTS_TYPE_CODES.fetch(default_rights, 'Unrecognized default rights value')
|
286
|
-
end
|
287
|
-
|
288
275
|
# Set the rights in default object rights
|
289
276
|
# @param rights [String] Stanford, World, Dark, or None
|
290
277
|
def default_rights=(rights)
|
@@ -1,7 +1,6 @@
|
|
1
1
|
module Dor
|
2
2
|
module Identifiable
|
3
3
|
extend ActiveSupport::Concern
|
4
|
-
include SolrDocHelper
|
5
4
|
|
6
5
|
# ids for previous and current catkeys
|
7
6
|
CATKEY_TYPE_ID = 'catkey'.freeze
|
@@ -18,13 +17,13 @@ module Dor
|
|
18
17
|
@object_type = str
|
19
18
|
Dor.registered_classes[str] = self
|
20
19
|
end
|
21
|
-
end
|
22
20
|
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
# Overrides the method in ActiveFedora to mint a pid using SURI rather
|
22
|
+
# than the default Fedora sequence
|
23
|
+
def assign_pid(_obj)
|
24
|
+
return Dor::SuriService.mint_id if Dor::Config.suri.mint_ids
|
25
|
+
super
|
26
26
|
end
|
27
|
-
super
|
28
27
|
end
|
29
28
|
|
30
29
|
# helper method to get the tags as an array
|
@@ -38,44 +37,6 @@ module Dor
|
|
38
37
|
content_tag.size == 1 ? content_tag[0].split(':').last.strip : ''
|
39
38
|
end
|
40
39
|
|
41
|
-
## Module-level variables, shared between ALL mixin includers (and ALL *their* includers/extenders)!
|
42
|
-
## used for caching found values
|
43
|
-
@@collection_hash = {}
|
44
|
-
@@apo_hash = {}
|
45
|
-
|
46
|
-
def to_solr(solr_doc = {}, *args)
|
47
|
-
assert_content_model
|
48
|
-
solr_doc = super(solr_doc, *args)
|
49
|
-
|
50
|
-
solr_doc[Dor::INDEX_VERSION_FIELD] = Dor::VERSION
|
51
|
-
solr_doc['indexed_at_dtsi'] = Time.now.utc.xmlschema
|
52
|
-
datastreams.values.each do |ds|
|
53
|
-
add_solr_value(solr_doc, 'ds_specs', ds.datastream_spec_string, :string, [:symbol]) unless ds.new?
|
54
|
-
end
|
55
|
-
|
56
|
-
add_solr_value(solr_doc, 'title_sort', label, :string, [:stored_sortable])
|
57
|
-
|
58
|
-
rels_doc = Nokogiri::XML(datastreams['RELS-EXT'].content)
|
59
|
-
ns_hash = {'hydra' => 'http://projecthydra.org/ns/relations#', 'fedora' => 'info:fedora/fedora-system:def/relations-external#', 'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'}
|
60
|
-
apos = rels_doc.search('//rdf:RDF/rdf:Description/hydra:isGovernedBy', ns_hash)
|
61
|
-
collections = rels_doc.search('//rdf:RDF/rdf:Description/fedora:isMemberOfCollection', ns_hash)
|
62
|
-
solrize_related_obj_titles(solr_doc, apos, @@apo_hash, 'apo_title', 'nonhydrus_apo_title', 'hydrus_apo_title')
|
63
|
-
solrize_related_obj_titles(solr_doc, collections, @@collection_hash, 'collection_title', 'nonhydrus_collection_title', 'hydrus_collection_title')
|
64
|
-
solr_doc['public_dc_relation_tesim'] ||= solr_doc['collection_title_tesim'] if solr_doc['collection_title_tesim']
|
65
|
-
solr_doc['metadata_source_ssi'] = identity_metadata_source
|
66
|
-
solr_doc
|
67
|
-
end
|
68
|
-
|
69
|
-
# @return [String] calculated value for Solr index
|
70
|
-
def identity_metadata_source
|
71
|
-
if identityMetadata.otherId('catkey').first ||
|
72
|
-
identityMetadata.otherId('barcode').first
|
73
|
-
'Symphony'
|
74
|
-
else
|
75
|
-
'DOR'
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
40
|
# Convenience method
|
80
41
|
def source_id
|
81
42
|
identityMetadata.sourceId
|
@@ -268,40 +229,5 @@ module Dor
|
|
268
229
|
end
|
269
230
|
end
|
270
231
|
end
|
271
|
-
|
272
|
-
private
|
273
|
-
|
274
|
-
def solrize_related_obj_titles(solr_doc, relationships, title_hash, union_field_name, nonhydrus_field_name, hydrus_field_name)
|
275
|
-
# TODO: if you wanted to get a little fancier, you could also solrize a 2 level hierarchy and display using hierarchial facets, like
|
276
|
-
# ["SOURCE", "SOURCE : TITLE"] (e.g. ["Hydrus", "Hydrus : Special Collections"], see (exploded) tags in IdentityMetadataDS#to_solr).
|
277
|
-
title_type = :symbol # we'll get an _ssim because of the type
|
278
|
-
title_attrs = [:stored_searchable] # we'll also get a _tesim from this attr
|
279
|
-
relationships.each do |rel_node|
|
280
|
-
rel_druid = rel_node['rdf:resource']
|
281
|
-
next unless rel_druid # TODO: warning here would also be useful
|
282
|
-
rel_druid = rel_druid.gsub('info:fedora/', '')
|
283
|
-
|
284
|
-
# populate cache if necessary
|
285
|
-
unless title_hash.key?(rel_druid)
|
286
|
-
begin
|
287
|
-
related_obj = Dor.find(rel_druid)
|
288
|
-
related_obj_title = get_related_obj_display_title(related_obj, rel_druid)
|
289
|
-
is_from_hydrus = (related_obj && related_obj.tags.include?('Project : Hydrus'))
|
290
|
-
title_hash[rel_druid] = {'related_obj_title' => related_obj_title, 'is_from_hydrus' => is_from_hydrus}
|
291
|
-
rescue ActiveFedora::ObjectNotFoundError
|
292
|
-
# This may happen if the given APO or Collection does not exist (bad data)
|
293
|
-
title_hash[rel_druid] = {'related_obj_title' => rel_druid, 'is_from_hydrus' => false}
|
294
|
-
end
|
295
|
-
end
|
296
|
-
|
297
|
-
# cache should definitely be populated, so just use that to write solr field
|
298
|
-
if title_hash[rel_druid]['is_from_hydrus']
|
299
|
-
add_solr_value(solr_doc, hydrus_field_name, title_hash[rel_druid]['related_obj_title'], title_type, title_attrs)
|
300
|
-
else
|
301
|
-
add_solr_value(solr_doc, nonhydrus_field_name, title_hash[rel_druid]['related_obj_title'], title_type, title_attrs)
|
302
|
-
end
|
303
|
-
add_solr_value(solr_doc, union_field_name, title_hash[rel_druid]['related_obj_title'], title_type, title_attrs)
|
304
|
-
end
|
305
|
-
end
|
306
232
|
end
|
307
233
|
end
|
@@ -3,7 +3,6 @@ require 'equivalent-xml'
|
|
3
3
|
module Dor
|
4
4
|
module Processable
|
5
5
|
extend ActiveSupport::Concern
|
6
|
-
include SolrDocHelper
|
7
6
|
|
8
7
|
included do
|
9
8
|
has_metadata :name => 'workflows', :type => Dor::WorkflowDs, :label => 'Workflows', :control_group => 'E'
|
@@ -147,65 +146,6 @@ module Dor
|
|
147
146
|
result
|
148
147
|
end
|
149
148
|
|
150
|
-
# @return [String] text translation of the status code, minus any trailing parenthetical explanation
|
151
|
-
# e.g. 'In accessioning (described)' and 'In accessioning (described, published)' both return 'In accessioning'
|
152
|
-
def simplified_status_code_disp_txt(status_code)
|
153
|
-
STATUS_CODE_DISP_TXT[status_code].gsub(/\(.*\)$/, '').strip
|
154
|
-
end
|
155
|
-
|
156
|
-
def to_solr(solr_doc = {}, *args)
|
157
|
-
solr_doc = super(solr_doc, *args)
|
158
|
-
sortable_milestones = {}
|
159
|
-
current_version = '1'
|
160
|
-
begin
|
161
|
-
current_version = versionMetadata.current_version_id
|
162
|
-
rescue
|
163
|
-
end
|
164
|
-
current_version_num = current_version.to_i
|
165
|
-
|
166
|
-
if self.respond_to?('versionMetadata')
|
167
|
-
# add an entry with version id, tag and description for each version
|
168
|
-
while current_version_num > 0
|
169
|
-
new_val = "#{current_version_num};#{versionMetadata.tag_for_version(current_version_num.to_s)};#{versionMetadata.description_for_version(current_version_num.to_s)}"
|
170
|
-
add_solr_value(solr_doc, 'versions', new_val, :string, [:displayable])
|
171
|
-
current_version_num -= 1
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
milestones.each do |milestone|
|
176
|
-
timestamp = milestone[:at].utc.xmlschema
|
177
|
-
sortable_milestones[milestone[:milestone]] ||= []
|
178
|
-
sortable_milestones[milestone[:milestone]] << timestamp
|
179
|
-
milestone[:version] ||= current_version
|
180
|
-
solr_doc['lifecycle_ssim'] ||= []
|
181
|
-
solr_doc['lifecycle_ssim'] << milestone[:milestone]
|
182
|
-
add_solr_value(solr_doc, 'lifecycle', "#{milestone[:milestone]}:#{timestamp};#{milestone[:version]}", :symbol)
|
183
|
-
end
|
184
|
-
|
185
|
-
sortable_milestones.each do |milestone, unordered_dates|
|
186
|
-
dates = unordered_dates.sort
|
187
|
-
# create the published_dttsi and published_day fields and the like
|
188
|
-
dates.each do |date|
|
189
|
-
solr_doc["#{milestone}_dttsim"] ||= []
|
190
|
-
solr_doc["#{milestone}_dttsim"] << date unless solr_doc["#{milestone}_dttsim"].include?(date)
|
191
|
-
end
|
192
|
-
# fields for OAI havester to sort on: _dttsi is trie date +stored +indexed (single valued, i.e. sortable)
|
193
|
-
solr_doc["#{milestone}_earliest_dttsi"] = dates.first
|
194
|
-
solr_doc["#{milestone}_latest_dttsi" ] = dates.last
|
195
|
-
end
|
196
|
-
solr_doc['status_ssi'] = status # status is singular (i.e. the current one)
|
197
|
-
solr_doc['current_version_isi'] = current_version.to_i
|
198
|
-
solr_doc['modified_latest_dttsi'] = modified_date.to_datetime.utc.strftime('%FT%TZ')
|
199
|
-
add_solr_value(solr_doc, 'rights', rights, :string, [:symbol]) if self.respond_to? :rights
|
200
|
-
|
201
|
-
status_info_hash = status_info
|
202
|
-
status_code = status_info_hash[:status_code]
|
203
|
-
add_solr_value(solr_doc, 'processing_status_text', simplified_status_code_disp_txt(status_code), :string, [:stored_sortable])
|
204
|
-
solr_doc['processing_status_code_isi'] = status_code # no _isi in Solrizer's default descriptors
|
205
|
-
|
206
|
-
solr_doc
|
207
|
-
end
|
208
|
-
|
209
149
|
# Initilizes workflow for the object in the workflow service
|
210
150
|
# It will set the priorty of the new workflow to the current_priority if it is > 0
|
211
151
|
# It will set lane_id from the item's APO default workflow lane
|
@@ -3,50 +3,17 @@ require 'fileutils'
|
|
3
3
|
|
4
4
|
module Dor
|
5
5
|
module Publishable
|
6
|
+
extend Deprecation
|
6
7
|
extend ActiveSupport::Concern
|
8
|
+
self.deprecation_horizon = 'dor-services version 7.0.0'
|
7
9
|
|
8
10
|
# Compute the thumbnail for this object following the rules at https://consul.stanford.edu/display/chimera/The+Rules+of+Thumb
|
11
|
+
# Used by PublicXmlService
|
9
12
|
# @return [String] the computed thumb filename, with the druid prefix and a slash in front of it, e.g. oo000oo0001/filenamewith space.jp2
|
10
13
|
def thumb
|
11
|
-
|
12
|
-
cm = contentMetadata.ng_xml
|
13
|
-
mime_type_finder = "@mimetype='image/jp2' or @mimeType='image/jp2'" # allow the mimetype attribute to be lower or camelcase when searching to make it more robust
|
14
|
-
thumb_image=nil
|
15
|
-
|
16
|
-
# these are the finders we will use to search for a thumb resource in contentMetadata, they will be searched in the order provided, stopping when one is reached
|
17
|
-
thumb_xpath_finders = [
|
18
|
-
# first find a file of mimetype jp2 explicitly marked as a thumb in the resource type and with a thumb=yes attribute
|
19
|
-
{image_type: 'local', finder: "/contentMetadata/resource[@type='thumb' and @thumb='yes']/file[#{mime_type_finder}]"},
|
20
|
-
# same thing for external files
|
21
|
-
{image_type: 'external', finder: "/contentMetadata/resource[@type='thumb' and @thumb='yes']/externalFile[#{mime_type_finder}]"},
|
22
|
-
# next find any image or page resource types with the thumb=yes attribute of mimetype jp2
|
23
|
-
{image_type: 'local', finder: "/contentMetadata/resource[(@type='page' or @type='image') and @thumb='yes']/file[#{mime_type_finder}]"},
|
24
|
-
# same thing for external file
|
25
|
-
{image_type: 'external', finder: "/contentMetadata/resource[(@type='page' or @type='image') and @thumb='yes']/externalFile[#{mime_type_finder}]"},
|
26
|
-
# next find a file of mimetype jp2 and resource type=thumb but not marked with the thumb directive
|
27
|
-
{image_type: 'local', finder: "/contentMetadata/resource[@type='thumb']/file[#{mime_type_finder}]"},
|
28
|
-
# same thing for external file
|
29
|
-
{image_type: 'external', finder: "/contentMetadata/resource[@type='thumb']/externalFile[#{mime_type_finder}]"},
|
30
|
-
# finally find the first page or image resource of mimetype jp2
|
31
|
-
{image_type: 'local', finder: "/contentMetadata/resource[@type='page' or @type='image']/file[#{mime_type_finder}]"},
|
32
|
-
# same thing for external file
|
33
|
-
{image_type: 'external', finder: "/contentMetadata/resource[@type='page' or @type='image']/externalFile[#{mime_type_finder}]"}
|
34
|
-
]
|
35
|
-
|
36
|
-
thumb_xpath_finders.each do |search_path|
|
37
|
-
thumb_files = cm.xpath(search_path[:finder]) # look for a thumb
|
38
|
-
if thumb_files.size > 0 # if we find one, return the filename based on whether it is a local file or external file
|
39
|
-
if search_path[:image_type] == 'local'
|
40
|
-
thumb_image="#{remove_druid_prefix}/#{thumb_files[0]['id']}"
|
41
|
-
else
|
42
|
-
thumb_image="#{remove_druid_prefix(thumb_files[0]['objectId'])}/#{thumb_files[0]['fileId']}"
|
43
|
-
end
|
44
|
-
break # break out of the loop so we stop searching
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
thumb_image
|
14
|
+
ThumbnailService.new(self).thumb
|
49
15
|
end
|
16
|
+
deprecation_deprecate :thumb
|
50
17
|
|
51
18
|
# Return a URI encoded version of the thumb image for use by indexers (leaving the extension of the filename)
|
52
19
|
# @return [String] URI encoded version of the thumb with the druid prefix, e.g. oo000oo0001%2Ffilenamewith%20space.jp2
|
@@ -57,6 +24,7 @@ module Dor
|
|
57
24
|
thumb_filename=thumb_image.split(/#{pid_regex}[\/]/).last # everything after the druid
|
58
25
|
"#{thumb_druid}%2F#{ERB::Util.url_encode(thumb_filename)}"
|
59
26
|
end
|
27
|
+
deprecation_deprecate :encoded_thumb
|
60
28
|
|
61
29
|
# Return a full qualified thumbnail image URL if the thumb is computable
|
62
30
|
# @return [String] fully qualified image URL for the computed thumbnail, e.g. https://stacks.stanford.edu/image/iiif/oo000oo0001%2Ffilenamewith%20space/full
|
@@ -65,6 +33,7 @@ module Dor
|
|
65
33
|
thumb_basename=File.basename(encoded_thumb, File.extname(encoded_thumb)) # strip the extension for URL generation
|
66
34
|
"https://#{Dor::Config.stacks.host}/image/iiif/#{thumb_basename}/full/!400,400/0/default.jpg"
|
67
35
|
end
|
36
|
+
deprecation_deprecate :thumb_url
|
68
37
|
|
69
38
|
# strips away the relationships that should not be shown in public desc metadata
|
70
39
|
# @return [Nokogiri::XML]
|
@@ -304,20 +304,5 @@ module Dor
|
|
304
304
|
end
|
305
305
|
new_tags
|
306
306
|
end
|
307
|
-
|
308
|
-
def to_solr(solr_doc = {}, *args)
|
309
|
-
solr_doc = super(solr_doc, *args)
|
310
|
-
|
311
|
-
# TODO: sort of worried about the performance impact in bulk reindex
|
312
|
-
# situations, since released_for recurses all parent collections. jmartin 2015-07-14
|
313
|
-
released_for(true).each { |release_target, release_info|
|
314
|
-
add_solr_value(solr_doc, 'released_to', release_target, :symbol, []) if release_info['release']
|
315
|
-
}
|
316
|
-
|
317
|
-
# TODO: need to solrize whether item is released to purl? does released_for return that?
|
318
|
-
# logic is: "True when there is a published lifecycle and Access Rights is anything but Dark"
|
319
|
-
|
320
|
-
solr_doc
|
321
|
-
end
|
322
307
|
end
|
323
308
|
end
|
data/lib/dor/models/item.rb
CHANGED
@@ -4,12 +4,21 @@ module Dor
|
|
4
4
|
include Embargoable
|
5
5
|
include Publishable
|
6
6
|
include Itemizable
|
7
|
+
include Preservable
|
7
8
|
include Assembleable
|
8
9
|
include Contentable
|
9
10
|
include Geoable
|
10
11
|
include Releaseable
|
11
12
|
|
12
13
|
has_object_type 'item'
|
14
|
+
|
15
|
+
self.resource_indexer = CompositeIndexer.new(
|
16
|
+
DataIndexer,
|
17
|
+
DescribableIndexer,
|
18
|
+
IdentifiableIndexer,
|
19
|
+
ProcessableIndexer,
|
20
|
+
ReleasableIndexer
|
21
|
+
)
|
13
22
|
end
|
14
23
|
end
|
15
24
|
|
data/lib/dor/models/set.rb
CHANGED
@@ -4,5 +4,12 @@ module Dor
|
|
4
4
|
|
5
5
|
has_many :members, :property => :is_member_of_collection, :class_name => 'ActiveFedora::Base'
|
6
6
|
has_object_type 'set'
|
7
|
+
|
8
|
+
self.resource_indexer = CompositeIndexer.new(
|
9
|
+
DataIndexer,
|
10
|
+
DescribableIndexer,
|
11
|
+
IdentifiableIndexer,
|
12
|
+
ProcessableIndexer
|
13
|
+
)
|
7
14
|
end
|
8
15
|
end
|
@@ -8,6 +8,13 @@ module Dor
|
|
8
8
|
has_object_type 'workflow'
|
9
9
|
has_metadata :name => 'workflowDefinition', :type => Dor::WorkflowDefinitionDs, :label => 'Workflow Definition'
|
10
10
|
|
11
|
+
self.resource_indexer = CompositeIndexer.new(
|
12
|
+
DataIndexer,
|
13
|
+
DescribableIndexer,
|
14
|
+
IdentifiableIndexer,
|
15
|
+
ProcessableIndexer
|
16
|
+
)
|
17
|
+
|
11
18
|
def self.find_by_name(name)
|
12
19
|
Dor::WorkflowObject.where(Solrizer.solr_name('workflow_name', :symbol) => name).first
|
13
20
|
end
|
@@ -38,10 +45,6 @@ module Dor
|
|
38
45
|
datastreams['workflowDefinition']
|
39
46
|
end
|
40
47
|
|
41
|
-
def graph(*args)
|
42
|
-
definition.graph *args
|
43
|
-
end
|
44
|
-
|
45
48
|
def generate_initial_workflow
|
46
49
|
datastreams['workflowDefinition'].initial_workflow
|
47
50
|
end
|
@@ -5,7 +5,7 @@ module Dor
|
|
5
5
|
def initialize(object)
|
6
6
|
@object = object
|
7
7
|
end
|
8
|
-
|
8
|
+
|
9
9
|
def to_xml
|
10
10
|
pub = Nokogiri::XML('<publicObject/>').root
|
11
11
|
pub['id'] = object.pid
|
@@ -21,8 +21,10 @@ module Dor
|
|
21
21
|
pub.add_child(Nokogiri::XML(object.generate_public_desc_md).root)
|
22
22
|
pub.add_child(release_xml.root) unless release_xml.xpath('//release').children.size == 0 # If there are no release_tags, this prevents an empty <releaseData/> from being added
|
23
23
|
# Note we cannot base this on if an individual object has release tags or not, because the collection may cause one to be generated for an item,
|
24
|
-
# so we need to calculate it and then look at the final result.
|
25
|
-
|
24
|
+
# so we need to calculate it and then look at the final result.
|
25
|
+
|
26
|
+
thumb = ThumbnailService.new(object).thumb
|
27
|
+
pub.add_child(Nokogiri("<thumb>#{thumb}</thumb>").root) unless thumb.nil?
|
26
28
|
|
27
29
|
new_pub = Nokogiri::XML(pub.to_xml) { |x| x.noblanks }
|
28
30
|
new_pub.encoding = 'UTF-8'
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Dor
|
2
|
+
# Responsible for finding a path to a thumbnail based on the contentMetadata of an object
|
3
|
+
class ThumbnailService
|
4
|
+
# allow the mimetype attribute to be lower or camelcase when searching to make it more robust
|
5
|
+
MIME_TYPE_FINDER = "@mimetype='image/jp2' or @mimeType='image/jp2'".freeze
|
6
|
+
|
7
|
+
# these are the finders we will use to search for a thumb resource in contentMetadata, they will be searched in the order provided, stopping when one is reached
|
8
|
+
THUMB_XPATH_FINDERS = [
|
9
|
+
# first find a file of mimetype jp2 explicitly marked as a thumb in the resource type and with a thumb=yes attribute
|
10
|
+
{ image_type: 'local', finder: "/contentMetadata/resource[@type='thumb' and @thumb='yes']/file[#{MIME_TYPE_FINDER}]"},
|
11
|
+
# same thing for external files
|
12
|
+
{ image_type: 'external', finder: "/contentMetadata/resource[@type='thumb' and @thumb='yes']/externalFile[#{MIME_TYPE_FINDER}]"},
|
13
|
+
# next find any image or page resource types with the thumb=yes attribute of mimetype jp2
|
14
|
+
{ image_type: 'local', finder: "/contentMetadata/resource[(@type='page' or @type='image') and @thumb='yes']/file[#{MIME_TYPE_FINDER}]"},
|
15
|
+
# same thing for external file
|
16
|
+
{ image_type: 'external', finder: "/contentMetadata/resource[(@type='page' or @type='image') and @thumb='yes']/externalFile[#{MIME_TYPE_FINDER}]"},
|
17
|
+
# next find a file of mimetype jp2 and resource type=thumb but not marked with the thumb directive
|
18
|
+
{ image_type: 'local', finder: "/contentMetadata/resource[@type='thumb']/file[#{MIME_TYPE_FINDER}]"},
|
19
|
+
# same thing for external file
|
20
|
+
{ image_type: 'external', finder: "/contentMetadata/resource[@type='thumb']/externalFile[#{MIME_TYPE_FINDER}]"},
|
21
|
+
# finally find the first page or image resource of mimetype jp2
|
22
|
+
{ image_type: 'local', finder: "/contentMetadata/resource[@type='page' or @type='image']/file[#{MIME_TYPE_FINDER}]"},
|
23
|
+
# same thing for external file
|
24
|
+
{ image_type: 'external', finder: "/contentMetadata/resource[@type='page' or @type='image']/externalFile[#{MIME_TYPE_FINDER}]"}
|
25
|
+
].freeze
|
26
|
+
|
27
|
+
# @params [Dor::Item] object
|
28
|
+
def initialize(object)
|
29
|
+
@object = object
|
30
|
+
end
|
31
|
+
|
32
|
+
attr_reader :object
|
33
|
+
|
34
|
+
# @return [String] the computed thumb filename, with the druid prefix and a slash in front of it, e.g. oo000oo0001/filenamewith space.jp2
|
35
|
+
def thumb
|
36
|
+
return unless object.respond_to?(:contentMetadata) && object.contentMetadata.present?
|
37
|
+
cm = object.contentMetadata.ng_xml
|
38
|
+
thumb_image = nil
|
39
|
+
|
40
|
+
THUMB_XPATH_FINDERS.each do |search_path|
|
41
|
+
thumb_files = cm.xpath(search_path[:finder]) # look for a thumb
|
42
|
+
next if thumb_files.empty?
|
43
|
+
# if we find one, return the filename based on whether it is a local file or external file
|
44
|
+
thumb_image = if search_path[:image_type] == 'local'
|
45
|
+
"#{object.remove_druid_prefix}/#{thumb_files[0]['id']}"
|
46
|
+
else
|
47
|
+
"#{object.remove_druid_prefix(thumb_files[0]['objectId'])}/#{thumb_files[0]['fileId']}"
|
48
|
+
end
|
49
|
+
break # break out of the loop so we stop searching
|
50
|
+
end
|
51
|
+
|
52
|
+
thumb_image
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/dor/version.rb
CHANGED
@@ -54,13 +54,6 @@ module Workflow
|
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
|
-
def graph(parent = nil, dir = nil)
|
58
|
-
wf_definition = definition
|
59
|
-
result = wf_definition ? Workflow::Graph.from_processes(wf_definition.repo, wf_definition.name, processes, parent) : nil
|
60
|
-
result['rankdir'] = dir || 'TB' unless result.nil?
|
61
|
-
result
|
62
|
-
end
|
63
|
-
|
64
57
|
def [](value)
|
65
58
|
processes.find { |p| p.name == value }
|
66
59
|
end
|
data/lib/dor/workflow/process.rb
CHANGED
@@ -89,6 +89,7 @@ module Workflow
|
|
89
89
|
# @param info [Hash,Nokogiri::XML::Element,NilClass]
|
90
90
|
# @param new_owner [Dor::Workflow::Document]
|
91
91
|
def update!(info, new_owner)
|
92
|
+
raise ArgumentError, 'Owner can not be nil. It must be an instance of Dor::Workflow::Document' unless new_owner
|
92
93
|
@owner = new_owner
|
93
94
|
return self if info.nil?
|
94
95
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dor-services
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 6.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michael Klein
|
@@ -14,7 +14,7 @@ authors:
|
|
14
14
|
autorequire:
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
|
-
date: 2018-11-
|
17
|
+
date: 2018-11-28 00:00:00.000000000 Z
|
18
18
|
dependencies:
|
19
19
|
- !ruby/object:Gem::Dependency
|
20
20
|
name: active-fedora
|
@@ -70,6 +70,20 @@ dependencies:
|
|
70
70
|
- - "~>"
|
71
71
|
- !ruby/object:Gem::Version
|
72
72
|
version: 0.2.7
|
73
|
+
- !ruby/object:Gem::Dependency
|
74
|
+
name: deprecation
|
75
|
+
requirement: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - "~>"
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '0'
|
80
|
+
type: :runtime
|
81
|
+
prerelease: false
|
82
|
+
version_requirements: !ruby/object:Gem::Requirement
|
83
|
+
requirements:
|
84
|
+
- - "~>"
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '0'
|
73
87
|
- !ruby/object:Gem::Dependency
|
74
88
|
name: equivalent-xml
|
75
89
|
requirement: !ruby/object:Gem::Requirement
|
@@ -220,20 +234,6 @@ dependencies:
|
|
220
234
|
- - "~>"
|
221
235
|
- !ruby/object:Gem::Version
|
222
236
|
version: 0.3.0
|
223
|
-
- !ruby/object:Gem::Dependency
|
224
|
-
name: ruby-graphviz
|
225
|
-
requirement: !ruby/object:Gem::Requirement
|
226
|
-
requirements:
|
227
|
-
- - ">="
|
228
|
-
- !ruby/object:Gem::Version
|
229
|
-
version: '0'
|
230
|
-
type: :runtime
|
231
|
-
prerelease: false
|
232
|
-
version_requirements: !ruby/object:Gem::Requirement
|
233
|
-
requirements:
|
234
|
-
- - ">="
|
235
|
-
- !ruby/object:Gem::Version
|
236
|
-
version: '0'
|
237
237
|
- !ruby/object:Gem::Dependency
|
238
238
|
name: rubydora
|
239
239
|
requirement: !ruby/object:Gem::Requirement
|
@@ -588,6 +588,13 @@ files:
|
|
588
588
|
- lib/dor/datastreams/workflow_definition_ds.rb
|
589
589
|
- lib/dor/datastreams/workflow_ds.rb
|
590
590
|
- lib/dor/exceptions.rb
|
591
|
+
- lib/dor/indexers/composite_indexer.rb
|
592
|
+
- lib/dor/indexers/data_indexer.rb
|
593
|
+
- lib/dor/indexers/describable_indexer.rb
|
594
|
+
- lib/dor/indexers/editable_indexer.rb
|
595
|
+
- lib/dor/indexers/identifiable_indexer.rb
|
596
|
+
- lib/dor/indexers/processable_indexer.rb
|
597
|
+
- lib/dor/indexers/releasable_indexer.rb
|
591
598
|
- lib/dor/models/abstract.rb
|
592
599
|
- lib/dor/models/admin_policy_object.rb
|
593
600
|
- lib/dor/models/agreement.rb
|
@@ -629,6 +636,7 @@ files:
|
|
629
636
|
- lib/dor/services/search_service.rb
|
630
637
|
- lib/dor/services/suri_service.rb
|
631
638
|
- lib/dor/services/technical_metadata_service.rb
|
639
|
+
- lib/dor/services/thumbnail_service.rb
|
632
640
|
- lib/dor/utils/hydrus_shims.rb
|
633
641
|
- lib/dor/utils/ng_tidy.rb
|
634
642
|
- lib/dor/utils/predicate_patch.rb
|
@@ -636,7 +644,6 @@ files:
|
|
636
644
|
- lib/dor/utils/solr_doc_helper.rb
|
637
645
|
- lib/dor/version.rb
|
638
646
|
- lib/dor/workflow/document.rb
|
639
|
-
- lib/dor/workflow/graph.rb
|
640
647
|
- lib/dor/workflow/process.rb
|
641
648
|
- lib/tasks/rdoc.rake
|
642
649
|
homepage:
|
data/lib/dor/workflow/graph.rb
DELETED
@@ -1,163 +0,0 @@
|
|
1
|
-
require 'graphviz'
|
2
|
-
|
3
|
-
module Dor
|
4
|
-
module Workflow
|
5
|
-
class Graph
|
6
|
-
|
7
|
-
FILL_COLORS = { 'waiting' => 'white', 'ready' => 'white', 'error' => '#8B0000', 'blocked' => 'white', 'completed' => 'darkgreen', 'unknown' => '#CFCFCF' }.freeze
|
8
|
-
TEXT_COLORS = { 'waiting' => 'black', 'ready' => 'black', 'error' => 'white', 'blocked' => '#8B0000', 'completed' => 'white', 'unknown' => 'black' }.freeze
|
9
|
-
PATTERNS = { 'waiting' => 'diagonals', 'ready' => 'filled', 'error' => 'filled', 'blocked' => 'diagonals', 'completed' => 'filled', 'unknown' => 'filled' }.freeze
|
10
|
-
RESERVED_KEYS = %w(repository name).freeze
|
11
|
-
|
12
|
-
attr_reader :repo, :name, :processes, :graph, :root
|
13
|
-
|
14
|
-
def self.from_config(name, config, parent = nil)
|
15
|
-
wf = new(config['repository'], name, parent)
|
16
|
-
config.keys.each { |p| wf.add_process(p.to_s) unless RESERVED_KEYS.include?(p) }
|
17
|
-
config.keys.each { |p|
|
18
|
-
next unless wf.processes[p]
|
19
|
-
Array(config[p]['prerequisite']).each { |prereq|
|
20
|
-
prereq.sub!(/^#{config['repository']}:#{name}:/e, '')
|
21
|
-
if wf.processes[prereq]
|
22
|
-
wf.processes[p].depends_on(wf.processes[prereq])
|
23
|
-
else
|
24
|
-
wf.processes[p].depends_on(wf.add_process(prereq).set_status('external'))
|
25
|
-
end
|
26
|
-
}
|
27
|
-
}
|
28
|
-
wf.finish
|
29
|
-
wf
|
30
|
-
end
|
31
|
-
|
32
|
-
def self.from_processes(repo, name, processes, parent = nil)
|
33
|
-
wf = new(repo, name, parent)
|
34
|
-
processes.each { |p|
|
35
|
-
wf.add_process(p.name).status = p.state || 'unknown'
|
36
|
-
}
|
37
|
-
processes.each { |p|
|
38
|
-
p.prerequisite.each { |prereq|
|
39
|
-
prereq.sub!(/^#{repo}:#{name}:/e, '')
|
40
|
-
if wf.processes[prereq]
|
41
|
-
wf.processes[p.name].depends_on(wf.processes[prereq])
|
42
|
-
else
|
43
|
-
wf.processes[p.name].depends_on(wf.add_process(prereq).set_status('external'))
|
44
|
-
end
|
45
|
-
}
|
46
|
-
}
|
47
|
-
wf.finish
|
48
|
-
wf
|
49
|
-
end
|
50
|
-
|
51
|
-
def initialize(repo, name, parent = nil)
|
52
|
-
@repo = repo
|
53
|
-
@name = name
|
54
|
-
if parent.nil?
|
55
|
-
@graph = GraphViz.new(qname)
|
56
|
-
@root = add_nodes(name)
|
57
|
-
else
|
58
|
-
@graph = parent.subgraph(qname)
|
59
|
-
@root = parent.add_nodes(name)
|
60
|
-
end
|
61
|
-
@graph[:truecolor => true]
|
62
|
-
@root.shape = 'plaintext'
|
63
|
-
@processes = {}
|
64
|
-
end
|
65
|
-
|
66
|
-
def qname
|
67
|
-
[@repo, @name].join(':')
|
68
|
-
end
|
69
|
-
|
70
|
-
def add_process(name, external = false)
|
71
|
-
pqname = name.split(/:/).length == 3 ? name : [qname, name].join(':')
|
72
|
-
p = Process.new(self, pqname, name)
|
73
|
-
@processes[name] = p
|
74
|
-
p
|
75
|
-
end
|
76
|
-
|
77
|
-
def finish
|
78
|
-
@processes.values.each do |process|
|
79
|
-
process.node.fontname = 'Helvetica'
|
80
|
-
if process.id =~ %r{^#{qname}} && process.prerequisites.length == 0
|
81
|
-
(@root << process.node)[:arrowhead => 'none', :arrowtail => 'none', :dir => 'both', :style => 'invisible']
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
@root.fontname = 'Helvetica'
|
86
|
-
self
|
87
|
-
end
|
88
|
-
|
89
|
-
def inspect
|
90
|
-
"#{to_s[0..-2]} #{repo}:#{name} (#{processes.keys.join(', ')})>"
|
91
|
-
end
|
92
|
-
|
93
|
-
def method_missing(sym, *args)
|
94
|
-
if @graph.respond_to?(sym)
|
95
|
-
@graph.send(sym, *args)
|
96
|
-
else
|
97
|
-
super
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
class Process
|
102
|
-
|
103
|
-
attr_reader :name, :status, :node, :prerequisites
|
104
|
-
|
105
|
-
def initialize(graph, id, name)
|
106
|
-
@name = name
|
107
|
-
@graph = graph
|
108
|
-
@node = @graph.add_nodes(id)
|
109
|
-
@node.shape = 'box'
|
110
|
-
@node.label = name
|
111
|
-
@prerequisites = []
|
112
|
-
set_status('unknown')
|
113
|
-
end
|
114
|
-
|
115
|
-
def id
|
116
|
-
@node.id
|
117
|
-
end
|
118
|
-
|
119
|
-
def status=(s)
|
120
|
-
@status = s
|
121
|
-
if s == 'external'
|
122
|
-
@node.fillcolor = 'gray'
|
123
|
-
@node.fontcolor = 'black'
|
124
|
-
@node.style = 'dashed'
|
125
|
-
else
|
126
|
-
@node.fillcolor = FILL_COLORS[s] || 'yellow'
|
127
|
-
@node.fontcolor = TEXT_COLORS[s]
|
128
|
-
@node.style = PATTERNS[s]
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
def set_status(s)
|
133
|
-
self.status = s
|
134
|
-
self
|
135
|
-
end
|
136
|
-
|
137
|
-
def depends_on(*processes)
|
138
|
-
wf1 = id.split(/:/)[0..1].join(':')
|
139
|
-
processes.each { |process|
|
140
|
-
wf2 = process.id.split(/:/)[0..1].join(':')
|
141
|
-
edge = (process.node << @node)
|
142
|
-
edge.dir = 'both'
|
143
|
-
edge.arrowhead = 'none'
|
144
|
-
edge.arrowtail = 'none'
|
145
|
-
edge.style = 'dashed' if wf1 != wf2
|
146
|
-
prerequisites << process
|
147
|
-
}
|
148
|
-
self
|
149
|
-
end
|
150
|
-
|
151
|
-
def same_as(process)
|
152
|
-
@node = process.node
|
153
|
-
end
|
154
|
-
|
155
|
-
def all_prerequisites
|
156
|
-
prerequisites.collect { |p| p.all_prerequisites + [p.name] }.flatten.uniq
|
157
|
-
end
|
158
|
-
|
159
|
-
end
|
160
|
-
|
161
|
-
end
|
162
|
-
end
|
163
|
-
end
|