dor-services 5.32.1 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|