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

Sign up to get free protection for your applications and to get access to all the features.
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