ddr-models 3.0.0.beta.3 → 3.0.0.beta.4

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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -3
  3. data/config/locales/ddr-models.en.yml +74 -0
  4. data/ddr-models.gemspec +3 -2
  5. data/lib/ddr/auth.rb +5 -2
  6. data/lib/ddr/auth/roles.rb +0 -2
  7. data/lib/ddr/auth/roles/role_set.rb +1 -0
  8. data/lib/ddr/derivatives/generators/png_generator.rb +1 -1
  9. data/lib/ddr/index.rb +6 -2
  10. data/lib/ddr/index/abstract_query_result.rb +1 -1
  11. data/lib/ddr/index/csv_options.rb +14 -0
  12. data/lib/ddr/index/csv_query_result.rb +39 -32
  13. data/lib/ddr/index/field.rb +11 -1
  14. data/lib/ddr/index/field_attribute.rb +22 -0
  15. data/lib/ddr/index/fields.rb +29 -20
  16. data/lib/ddr/index/filter.rb +81 -30
  17. data/lib/ddr/index/filters.rb +15 -10
  18. data/lib/ddr/index/query.rb +34 -13
  19. data/lib/ddr/index/query_builder.rb +150 -30
  20. data/lib/ddr/index/query_clause.rb +64 -19
  21. data/lib/ddr/index/query_params.rb +40 -0
  22. data/lib/ddr/index/solr_csv_options.rb +18 -0
  23. data/lib/ddr/index/sort_order.rb +28 -0
  24. data/lib/ddr/jobs.rb +5 -1
  25. data/lib/ddr/jobs/fits_file_characterization.rb +3 -41
  26. data/lib/ddr/jobs/fixity_check.rb +13 -0
  27. data/lib/ddr/jobs/job.rb +36 -0
  28. data/lib/ddr/jobs/queue.rb +27 -0
  29. data/lib/ddr/jobs/update_index.rb +13 -0
  30. data/lib/ddr/models.rb +20 -3
  31. data/lib/ddr/models/admin_set.rb +7 -3
  32. data/lib/ddr/models/contact.rb +19 -0
  33. data/lib/ddr/models/error.rb +3 -0
  34. data/lib/ddr/models/file_characterization.rb +37 -0
  35. data/lib/ddr/models/finding_aid.rb +35 -2
  36. data/lib/ddr/models/has_admin_metadata.rb +5 -1
  37. data/lib/ddr/models/has_content.rb +4 -2
  38. data/lib/ddr/models/indexing.rb +7 -0
  39. data/lib/ddr/models/licenses/license.rb +7 -3
  40. data/lib/ddr/models/solr_document.rb +2 -2
  41. data/lib/ddr/models/version.rb +1 -1
  42. data/lib/ddr/models/with_content_file.rb +37 -0
  43. data/lib/ddr/vocab/asset.rb +3 -0
  44. data/spec/derivatives/png_generator_spec.rb +8 -1
  45. data/spec/fixtures/arrow1rightred_e0.gif +0 -0
  46. data/spec/index/fields_spec.rb +197 -0
  47. data/spec/index/filter_spec.rb +155 -30
  48. data/spec/index/query_builder_spec.rb +116 -0
  49. data/spec/index/query_clause_spec.rb +58 -0
  50. data/spec/index/query_spec.rb +39 -10
  51. data/spec/jobs/fits_file_characterization_spec.rb +7 -43
  52. data/spec/jobs/fixity_check_spec.rb +22 -0
  53. data/spec/jobs/job_spec.rb +40 -0
  54. data/spec/jobs/update_index_spec.rb +22 -0
  55. data/spec/managers/derivatives_manager_spec.rb +15 -11
  56. data/spec/models/admin_set_spec.rb +28 -10
  57. data/spec/models/contact_spec.rb +42 -0
  58. data/spec/models/effective_license_spec.rb +17 -7
  59. data/spec/models/file_characterization_spec.rb +38 -0
  60. data/spec/models/finding_aid_spec.rb +31 -8
  61. data/spec/models/has_admin_metadata_spec.rb +8 -5
  62. data/spec/models/indexing_spec.rb +2 -0
  63. data/spec/models/license_spec.rb +31 -10
  64. data/spec/models/solr_document_spec.rb +23 -3
  65. data/spec/models/with_content_file_spec.rb +32 -0
  66. data/spec/spec_helper.rb +2 -0
  67. metadata +66 -28
  68. data/lib/ddr/contacts.rb +0 -25
  69. data/lib/ddr/index/query_value.rb +0 -18
  70. data/lib/ddr/models/access_controllable.rb +0 -12
  71. data/spec/auth/ldap_gateway_spec.rb +0 -9
  72. data/spec/contacts/contacts_spec.rb +0 -26
  73. data/spec/index/filters_spec.rb +0 -17
@@ -1,51 +1,96 @@
1
+ require "virtus"
2
+
1
3
  module Ddr::Index
2
4
  class QueryClause
5
+ include Virtus.value_object
6
+
7
+ ANY_FIELD = Field.new('*').freeze
8
+ ANY_VALUE = "[* TO *]"
9
+ QUOTE = '"'
10
+
11
+ TERM_QUERY = "{!term f=%{field}}%{value}"
12
+ STANDARD_QUERY = "%{field}:%{value}"
13
+ NEGATIVE_QUERY = "-%{field}:%{value}"
14
+ DISJUNCTION = "{!lucene q.op=OR df=%{field}}%{value}"
15
+
16
+ values do
17
+ attribute :field, FieldAttribute
18
+ attribute :value, String
19
+ attribute :quote_value, Boolean, default: false
20
+ attribute :template, String, default: STANDARD_QUERY
21
+ end
22
+
23
+ def to_s
24
+ template % { field: field, value: quote_value ? quote(value) : value }
25
+ end
3
26
 
4
- PRESENT = "[* TO *]"
5
- TERM = "{!term f=%s}%s"
6
- BEFORE_DAYS = "[* TO NOW-%sDAYS]"
27
+ def quote(value)
28
+ self.class.quote(value)
29
+ end
7
30
 
8
31
  class << self
9
- # Standard Solr query, no escaping applied
10
- def build(field, value)
11
- [field, value].join(":")
32
+
33
+ def quote(value)
34
+ # Derived from Blacklight::Solr::SearchBuilderBehavior#solr_param_quote
35
+ unless value =~ /\A[a-zA-Z0-9$_\-\^]+\z/
36
+ QUOTE + value.gsub("'", "\\\\\'").gsub('"', "\\\\\"") + QUOTE
37
+ else
38
+ value
39
+ end
12
40
  end
13
41
 
42
+ # Builds a query clause to retrieve the index document by unique key.
14
43
  def unique_key(value)
15
44
  term(UniqueKeyField.instance, value)
16
45
  end
17
46
  alias_method :id, :unique_key
18
- alias_method :pid, :unique_key
19
47
 
48
+ def where(field, value)
49
+ values = Array(value)
50
+ if values.size > 1
51
+ disjunction(field, values)
52
+ else
53
+ new(field: field, value: values.first, quote_value: true)
54
+ end
55
+ end
56
+
57
+ # Builds a query clause to filter where field does not have the given value.
20
58
  def negative(field, value)
21
- build "-#{field}", value
59
+ new(field: field, value: value, template: NEGATIVE_QUERY, quote_value: true)
22
60
  end
23
61
 
62
+ # Builds a query clause to filter where field is present (i.e, has any value)
24
63
  def present(field)
25
- build field, PRESENT
64
+ new(field: field, value: ANY_VALUE)
26
65
  end
27
66
 
67
+ # Builds a query clause to filter where field is NOT present (no values)
28
68
  def absent(field)
29
- negative field, PRESENT
69
+ new(field: field, value: ANY_VALUE, template: NEGATIVE_QUERY)
30
70
  end
31
71
 
32
- def or_values(field, *values)
33
- build field, QueryValue.or_values(*values)
72
+ # Builds a query clause to filter where field contains at least one of a set of values.
73
+ def disjunction(field, values)
74
+ value = values.map { |v| quote(v) }.join(" ")
75
+ new(field: field, value: value, template: DISJUNCTION)
34
76
  end
35
77
 
36
- def before(field, date_time)
37
- value = "[* TO %s]" % Ddr::Utils.solr_date(date_time)
38
- build field, value
78
+ # Builds a query clause to filter where date field value is earlier than a date/time value.
79
+ def before(field, value)
80
+ new(field: field, value: "[* TO %s]" % Ddr::Utils.solr_date(value))
39
81
  end
82
+ alias_method :before_date_time, :before
40
83
 
41
- def before_days(field, days)
42
- value = BEFORE_DAYS % days.to_i
43
- build field, value
84
+ # Builds a query clause to filter where date field value is earlier than a number of days before now.
85
+ def before_days(field, value)
86
+ new(field: field, value: "[* TO NOW-%iDAYS]" % value)
44
87
  end
45
88
 
89
+ # Builds a "term query" clause to filter where field contains value.
46
90
  def term(field, value)
47
- TERM % [field, value.gsub(/"/, '\"')]
91
+ new(field: field, value: value, template: TERM_QUERY)
48
92
  end
93
+
49
94
  end
50
95
 
51
96
  end
@@ -0,0 +1,40 @@
1
+ module Ddr::Index
2
+ class QueryParams
3
+
4
+ attr_reader :query
5
+
6
+ def initialize(query)
7
+ @query = query
8
+ end
9
+
10
+ def params
11
+ { q: q_param,
12
+ fq: filter_queries,
13
+ fl: fields,
14
+ sort: sort,
15
+ rows: rows,
16
+ }.select { |k, v| v.present? }
17
+ end
18
+
19
+ def q_param
20
+ query.q.to_s
21
+ end
22
+
23
+ def filter_queries
24
+ query.filter_clauses.map(&:to_s)
25
+ end
26
+
27
+ def fields
28
+ query.fields.join(",")
29
+ end
30
+
31
+ def sort
32
+ query.sort.join(",")
33
+ end
34
+
35
+ def rows
36
+ query.rows
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,18 @@
1
+ require "hashie"
2
+
3
+ module Ddr::Index
4
+ class SolrCSVOptions < Hashie::Trash
5
+
6
+ property "csv.header", from: :header, default: false
7
+ property "csv.separator", from: :col_sep, default: ","
8
+ property "csv.encapsulator", from: :quote_char, default: '"'
9
+ property "csv.mv.separator", from: :mv_sep, default: ","
10
+ property :wt, default: "csv"
11
+ property :rows
12
+
13
+ def params
14
+ to_h.reject { |k, v| v.nil? }
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,28 @@
1
+ require "virtus"
2
+
3
+ module Ddr::Index
4
+ class SortOrder
5
+ include Virtus.value_object
6
+
7
+ ASC = "asc"
8
+ DESC = "desc"
9
+
10
+ values do
11
+ attribute :field, FieldAttribute
12
+ attribute :order, String
13
+ end
14
+
15
+ def to_s
16
+ [ field, order ].join(" ")
17
+ end
18
+
19
+ def self.asc(field)
20
+ new(field: field, order: ASC)
21
+ end
22
+
23
+ def self.desc(field)
24
+ new(field: field, order: DESC)
25
+ end
26
+
27
+ end
28
+ end
data/lib/ddr/jobs.rb CHANGED
@@ -2,8 +2,12 @@ module Ddr
2
2
  module Jobs
3
3
  extend ActiveSupport::Autoload
4
4
 
5
- autoload :PermanentId
6
5
  autoload :FitsFileCharacterization
6
+ autoload :FixityCheck
7
+ autoload :Job
8
+ autoload :PermanentId
9
+ autoload :Queue
10
+ autoload :UpdateIndex
7
11
 
8
12
  autoload_at 'ddr/jobs/permanent_id' do
9
13
  autoload :MakeUnavailable
@@ -1,51 +1,13 @@
1
- require 'open3'
2
-
3
1
  module Ddr::Jobs
4
2
  class FitsFileCharacterization
3
+ extend Job
5
4
 
6
5
  @queue = :file_characterization
7
6
 
8
- EVENT_SUMMARY = 'FITS characterization of content file'.freeze
9
-
10
7
  def self.perform(pid)
11
8
  obj = ActiveFedora::Base.find(pid)
12
- tmp_filename = Ddr::Utils::sanitize_filename(obj.original_filename) || obj.content.default_file_name
13
- Dir.mktmpdir do |dir|
14
- infile = create_temp_infile(dir, tmp_filename, obj.content.content)
15
- fits_output, err, status = Open3.capture3(fits_command, '-i', infile)
16
- if status.success? && fits_output.present?
17
- obj.reload
18
- obj.fits.content = fits_output
19
- obj.save!
20
- end
21
- notify_event(pid, err, status)
22
- end
23
- end
24
-
25
- def self.fits_command
26
- File.join(Ddr::Models.fits_home, 'fits.sh')
27
- end
28
-
29
- def self.fits_version
30
- `#{fits_command} -v`.strip
31
- end
32
-
33
- private
34
-
35
- def self.create_temp_infile(dir, tmp_filename, content)
36
- temp_infile = File.join(dir, tmp_filename)
37
- File.open(temp_infile, 'wb') do |f|
38
- f.write content
39
- end
40
- temp_infile
41
- end
42
-
43
- def self.notify_event(pid, err, status)
44
- details = status.success? ? nil : err
45
- event_args = { pid: pid, summary: EVENT_SUMMARY, software: "fits #{fits_version}", detail: details }
46
- event_args[:outcome] = status.success? ? Ddr::Events::Event::SUCCESS : Ddr::Events::Event::FAILURE
47
- Ddr::Notifications.notify_event(:update, event_args)
9
+ Ddr::Models::FileCharacterization.call(obj)
48
10
  end
49
11
 
50
12
  end
51
- end
13
+ end
@@ -0,0 +1,13 @@
1
+ module Ddr::Jobs
2
+ class FixityCheck
3
+ extend Job
4
+
5
+ @queue = :fixity
6
+
7
+ def self.perform(id)
8
+ obj = ActiveFedora::Base.find(id)
9
+ obj.fixity_check
10
+ end
11
+
12
+ end
13
+ end
@@ -0,0 +1,36 @@
1
+ require "resque"
2
+
3
+ module Ddr::Jobs
4
+ module Job
5
+
6
+ def self.extended(base)
7
+ base.extend ClassMethods
8
+ end
9
+
10
+ module ClassMethods
11
+ # @return [Array<String>] list of object ids queued for this job type.
12
+ # @note Assumes that the object_id is the first argument of the .perform method.
13
+ def queued_object_ids(**args)
14
+ args[:type] = self
15
+ __queue__.jobs(**args).map { |job| job["args"].first }
16
+ end
17
+
18
+ protected
19
+
20
+ def method_missing(name, *args, &block)
21
+ # If .queue method not defined, do the right thing
22
+ if name == :queue
23
+ return Resque.queue_from_class(self)
24
+ end
25
+ super
26
+ end
27
+
28
+ private
29
+
30
+ def __queue__
31
+ Queue.new(queue)
32
+ end
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,27 @@
1
+ require "resque"
2
+
3
+ module Ddr::Jobs
4
+ class Queue
5
+
6
+ attr_reader :name
7
+
8
+ def initialize(name)
9
+ @name = name
10
+ end
11
+
12
+ def size
13
+ Resque.size(name)
14
+ end
15
+
16
+ # @return [Array<Hash>] jobs in the queue, optionally filtered by type,
17
+ # start position, and count.
18
+ def jobs(type: nil, start: 0, count: nil)
19
+ jobs = Resque.peek(name, start, count || size)
20
+ if type
21
+ jobs.select! { |job| job["class"] == type.to_s }
22
+ end
23
+ jobs
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,13 @@
1
+ module Ddr::Jobs
2
+ class UpdateIndex
3
+ extend Job
4
+
5
+ @queue = :index
6
+
7
+ def self.perform(id)
8
+ obj = ActiveFedora::Base.find(id)
9
+ obj.update_index
10
+ end
11
+
12
+ end
13
+ end
data/lib/ddr/models.rb CHANGED
@@ -7,15 +7,14 @@ require 'hydra/validations'
7
7
 
8
8
  module Ddr
9
9
  extend ActiveSupport::Autoload
10
+ extend Deprecation
10
11
 
11
12
  autoload :Actions
12
13
  autoload :Auth
13
- autoload :Contacts
14
14
  autoload :Datastreams
15
15
  autoload :Derivatives
16
16
  autoload :Events
17
17
  autoload :Index
18
- autoload :IndexFields
19
18
  autoload :Jobs
20
19
  autoload :Managers
21
20
  autoload :Metadata
@@ -23,19 +22,30 @@ module Ddr
23
22
  autoload :Utils
24
23
  autoload :Vocab
25
24
 
25
+ def self.const_missing(name)
26
+ if name == :IndexFields
27
+ Deprecation.warn(Ddr::Models, "`Ddr::IndexFields` is deprecated and will be removed in ddr-models 3.0." \
28
+ " Use `Ddr::Index::Fields` instead.")
29
+ Index::Fields
30
+ else
31
+ super
32
+ end
33
+ end
34
+
26
35
  module Models
27
36
  extend ActiveSupport::Autoload
28
37
 
29
- autoload :AccessControllable
30
38
  autoload :AdminSet
31
39
  autoload :AttachedFileProfile
32
40
  autoload :AttachedFilesProfile
33
41
  autoload :Base
34
42
  autoload :ChecksumInvalid, 'ddr/models/error'
43
+ autoload :Contact
35
44
  autoload :ContentModelError, 'ddr/models/error'
36
45
  autoload :DerivativeGenerationFailure, 'ddr/models/error'
37
46
  autoload :Error
38
47
  autoload :EventLoggable
48
+ autoload :FileCharacterization
39
49
  autoload :FileManagement
40
50
  autoload :FindingAid
41
51
  autoload :FixityCheckable
@@ -48,6 +58,7 @@ module Ddr
48
58
  autoload :HasStructMetadata
49
59
  autoload :HasThumbnail
50
60
  autoload :Indexing
61
+ autoload :NotFoundError, 'ddr/models/error'
51
62
  autoload :ObjectApi
52
63
  autoload :SolrDocument
53
64
  autoload :StructDiv
@@ -55,6 +66,7 @@ module Ddr
55
66
  autoload :UrlSafeId
56
67
  autoload :Validatable
57
68
  autoload :Validator
69
+ autoload :WithContentFile
58
70
  autoload :YearFacet
59
71
 
60
72
  autoload_under "licenses" do
@@ -113,6 +125,11 @@ module Ddr
113
125
  "http://library.duke.edu/rubenstein/findingaids/"
114
126
  end
115
127
 
128
+ # Application temp dir - defaults to system temp dir
129
+ mattr_accessor :tempdir do
130
+ Dir.tmpdir
131
+ end
132
+
116
133
  # Yields an object with module configuration accessors
117
134
  def self.configure
118
135
  yield self