geoblacklight_sidecar_images 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +4 -4
  3. data/.travis.yml +4 -4
  4. data/Gemfile +2 -2
  5. data/README.md +8 -7
  6. data/Rakefile +0 -3
  7. data/app/assets/stylesheets/geoblacklight_sidecar_images/_gblsci.scss +13 -0
  8. data/app/assets/stylesheets/geoblacklight_sidecar_images/application.scss +10 -0
  9. data/app/assets/stylesheets/geoblacklight_sidecar_images/modules/_results.scss +10 -0
  10. data/app/jobs/geoblacklight_sidecar_images/store_image_job.rb +18 -0
  11. data/app/models/{geoblacklight_sidecar_images/application_record.rb → application_record.rb} +0 -0
  12. data/{lib/generators/geoblacklight_sidecar_images/templates → app}/models/concerns/wms_rewrite_concern.rb +0 -0
  13. data/{lib/generators/geoblacklight_sidecar_images/templates → app}/models/sidecar_image_state_machine.rb +1 -1
  14. data/{lib/generators/geoblacklight_sidecar_images/templates → app}/models/sidecar_image_transition.rb +0 -0
  15. data/{lib/generators/geoblacklight_sidecar_images/templates → app}/models/solr_document_sidecar.rb +3 -3
  16. data/app/services/geoblacklight_sidecar_images/image_service.rb +226 -0
  17. data/app/services/geoblacklight_sidecar_images/image_service/dynamic_map_layer.rb +15 -0
  18. data/app/services/geoblacklight_sidecar_images/image_service/iiif.rb +17 -0
  19. data/app/services/geoblacklight_sidecar_images/image_service/image_map_layer.rb +17 -0
  20. data/app/services/geoblacklight_sidecar_images/image_service/tiled_map_layer.rb +15 -0
  21. data/app/services/geoblacklight_sidecar_images/image_service/wms.rb +28 -0
  22. data/geoblacklight_sidecar_images.gemspec +5 -5
  23. data/lib/generators/geoblacklight_sidecar_images/install_generator.rb +9 -9
  24. data/lib/generators/geoblacklight_sidecar_images/jobs_generator.rb +1 -6
  25. data/lib/generators/geoblacklight_sidecar_images/models_generator.rb +0 -28
  26. data/lib/generators/geoblacklight_sidecar_images/templates/gblsci.scss +3 -0
  27. data/lib/generators/geoblacklight_sidecar_images/templates/views/catalog/_index_split_default.html.erb +28 -28
  28. data/lib/geoblacklight_sidecar_images/version.rb +1 -1
  29. data/lib/tasks/geoblacklight_sidecar_images_tasks.rake +10 -13
  30. data/solr/conf/schema.xml +11 -3
  31. data/solr/conf/solrconfig.xml +4 -4
  32. data/spec/fixtures/files/README.md +40 -0
  33. data/spec/fixtures/files/actual-papermap1.json +1 -1
  34. data/spec/fixtures/files/actual-point1.json +26 -13
  35. data/spec/fixtures/files/actual-polygon1.json +1 -1
  36. data/spec/fixtures/files/actual-raster1.json +6 -5
  37. data/spec/fixtures/files/bbox-spans-180.json +20 -0
  38. data/spec/fixtures/files/cornell_html_metadata.json +42 -0
  39. data/spec/fixtures/files/esri-dynamic-layer-all-layers.json +36 -34
  40. data/spec/fixtures/files/esri-dynamic-layer-single-layer.json +30 -27
  41. data/spec/fixtures/files/esri-feature-layer.json +37 -24
  42. data/spec/fixtures/files/esri-image-map-layer.json +39 -18
  43. data/spec/fixtures/files/iiif-eastern-hemisphere.json +30 -0
  44. data/spec/fixtures/files/index-map-polygon-no-downloadurl.json +37 -0
  45. data/spec/fixtures/files/index-map-polygon.json +37 -0
  46. data/spec/fixtures/files/index-map-stanford.json +37 -0
  47. data/spec/fixtures/files/index_map_point.json +27 -0
  48. data/spec/fixtures/files/princeton-child1.json +30 -0
  49. data/spec/fixtures/files/princeton-child2.json +30 -0
  50. data/spec/fixtures/files/princeton-child3.json +30 -0
  51. data/spec/fixtures/files/princeton-child4.json +30 -0
  52. data/spec/fixtures/files/princeton-parent.json +25 -0
  53. data/spec/fixtures/files/public_direct_download.json +13 -7
  54. data/spec/fixtures/files/public_polygon_mit.json +15 -27
  55. data/spec/fixtures/files/restricted-line.json +7 -6
  56. data/spec/fixtures/files/umn_iiif_jpg.json +1 -2
  57. data/spec/fixtures/files/umn_metro_result1.json +30 -26
  58. data/spec/fixtures/files/umn_state_result1.json +25 -18
  59. data/spec/fixtures/files/umn_state_result2.json +27 -25
  60. data/spec/fixtures/files/uva_slug_colon.json +25 -0
  61. data/spec/jobs/store_image_job_spec.rb +2 -2
  62. data/spec/models/solr_document_sidecar_spec.rb +1 -1
  63. data/spec/services/image_service_spec.rb +1 -1
  64. data/spec/test_app_templates/lib/generators/test_app_generator.rb +2 -2
  65. data/template.rb +3 -3
  66. metadata +55 -30
  67. data/app/assets/config/geoblacklight_sidecar_images_manifest.js +0 -2
  68. data/app/assets/images/geoblacklight_sidecar_images/.keep +0 -0
  69. data/app/assets/javascripts/geoblacklight_sidecar_images/application.js +0 -13
  70. data/app/assets/stylesheets/geoblacklight_sidecar_images/application.css +0 -15
  71. data/lib/generators/geoblacklight_sidecar_images/services_generator.rb +0 -19
  72. data/lib/generators/geoblacklight_sidecar_images/templates/assets/javascripts/geoblacklight.js +0 -9
  73. data/lib/generators/geoblacklight_sidecar_images/templates/assets/stylesheets/geoblacklight.scss +0 -3
  74. data/lib/generators/geoblacklight_sidecar_images/templates/jobs/store_image_job.rb +0 -17
  75. data/lib/generators/geoblacklight_sidecar_images/templates/services/image_service.rb +0 -224
  76. data/lib/generators/geoblacklight_sidecar_images/templates/services/image_service/dynamic_map_layer.rb +0 -13
  77. data/lib/generators/geoblacklight_sidecar_images/templates/services/image_service/iiif.rb +0 -15
  78. data/lib/generators/geoblacklight_sidecar_images/templates/services/image_service/image_map_layer.rb +0 -15
  79. data/lib/generators/geoblacklight_sidecar_images/templates/services/image_service/tiled_map_layer.rb +0 -13
  80. data/lib/generators/geoblacklight_sidecar_images/templates/services/image_service/wms.rb +0 -26
@@ -1,2 +0,0 @@
1
- //= link_directory ../javascripts/geoblacklight_sidecar_images .js
2
- //= link_directory ../stylesheets/geoblacklight_sidecar_images .css
File without changes
@@ -1,13 +0,0 @@
1
- // This is a manifest file that'll be compiled into application.js, which will include all the files
2
- // listed below.
3
- //
4
- // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
- // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
- //
7
- // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
- // compiled file. JavaScript code in this file should be added after the last require_* statement.
9
- //
10
- // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
- // about supported directives.
12
- //
13
- //= require_tree .
@@ -1,15 +0,0 @@
1
- /*
2
- * This is a manifest file that'll be compiled into application.css, which will include all the files
3
- * listed below.
4
- *
5
- * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
- * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
- *
8
- * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
- * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
- * files in this directory. Styles in this file should be added after the last require_* statement.
11
- * It is generally better to create a new file per style scope.
12
- *
13
- *= require_tree .
14
- *= require_self
15
- */
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'rails/generators'
4
-
5
- module GeoblacklightSidecarImages
6
- class ServicesGenerator < Rails::Generators::Base
7
- source_root File.expand_path('templates', __dir__)
8
-
9
- desc <<-DESCRIPTION
10
- This generator makes the following changes to your application:
11
- 1. Creates an app/services directory
12
- 2. Creates service models within the app/services directory
13
- DESCRIPTION
14
-
15
- def create_services
16
- directory 'services', 'app/services'
17
- end
18
- end
19
- end
@@ -1,9 +0,0 @@
1
- //= require geoblacklight/geoblacklight
2
- //= require geoblacklight/basemaps
3
- //= require geoblacklight/controls
4
- //= require geoblacklight/viewers
5
- //= require geoblacklight/modules
6
- //= require geoblacklight/downloaders
7
- //= require leaflet-iiif
8
- //= require esri-leaflet
9
- //= require readmore.min
@@ -1,3 +0,0 @@
1
- /*
2
- *= require geoblacklight/application
3
- */
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class StoreImageJob < ApplicationJob
4
- queue_as :default
5
-
6
- def perform(solr_document_id)
7
- cat = CatalogController.new
8
- _response, document = cat.fetch(solr_document_id)
9
-
10
- metadata = {}
11
- metadata['solr_doc_id'] = document.id
12
- metadata['solr_version'] = document.sidecar.version
13
-
14
- document.sidecar.image_state.transition_to!(:queued, metadata)
15
- ImageService.new(document).store
16
- end
17
- end
@@ -1,224 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'addressable/uri'
4
- require 'mimemagic'
5
-
6
- class ImageService
7
- attr_reader :document
8
- attr_writer :metadata, :logger
9
-
10
- def initialize(document)
11
- @document = document
12
-
13
- @metadata = {}
14
- @metadata['solr_doc_id'] = document.id
15
- @metadata['solr_version'] = @document.sidecar.version
16
- @metadata['placeheld'] = false
17
-
18
- @document.sidecar.image_state.transition_to!(:processing, @metadata)
19
-
20
- @logger ||= ActiveSupport::TaggedLogging.new(
21
- Logger.new(
22
- Rails.root.join('log', "image_service_#{Rails.env}.log")
23
- )
24
- )
25
- end
26
-
27
- # Stores the document's image in ActiveStorage
28
- # @return [Boolean]
29
- #
30
- def store
31
- # Gentle hands
32
- sleep(1)
33
-
34
- io_file = image_tempfile(@document.id)
35
-
36
- if io_file.nil? || @metadata['placeheld'] == true
37
- @document.sidecar.image_state.transition_to!(:placeheld, @metadata)
38
- else
39
- attach_io(io_file)
40
- end
41
-
42
- log_output
43
- rescue Exception => invalid
44
- @metadata['exception'] = invalid.inspect
45
- @document.sidecar.image_state.transition_to!(:failed, @metadata)
46
-
47
- log_output
48
- end
49
-
50
- private
51
-
52
- def image_tempfile(document_id)
53
- @metadata['viewer_protocol'] = @document.viewer_protocol
54
- @metadata['image_url'] = image_url
55
- @metadata['service_url'] = service_url
56
- @metadata['gblsi_thumbnail_uri'] = gblsi_thumbnail_uri
57
-
58
- return nil unless image_data && @metadata['placeheld'] == false
59
-
60
- temp_file = Tempfile.new([document_id, '.tmp'])
61
- temp_file.binmode
62
- temp_file.write(image_data)
63
- temp_file.rewind
64
-
65
- @metadata['image_tempfile'] = temp_file.inspect
66
- temp_file
67
- end
68
-
69
- def attach_io(io)
70
- # Remote content-type headers are untrustworthy
71
- # Pull the mimetype and file extension via MimeMagic
72
- mm = MimeMagic.by_magic(File.open(io))
73
-
74
- @metadata['MimeMagic_type'] = mm.type
75
- @metadata['MimeMagic_mediatype'] = mm.mediatype
76
- @metadata['MimeMagic_subtype'] = mm.subtype
77
-
78
- if mm.mediatype == 'image'
79
- @document.sidecar.image.attach(
80
- io: io,
81
- filename: "#{@document.id}.#{mm.subtype}",
82
- content_type: mm.type
83
- )
84
- @document.sidecar.image_state.transition_to!(:succeeded, @metadata)
85
- else
86
- @document.sidecar.image_state.transition_to!(:placeheld, @metadata)
87
- end
88
- end
89
-
90
- # Returns geoserver auth credentials if the document is a restriced Local WMS layer.
91
- def geoserver_credentials
92
- return unless restricted_wms_layer?
93
-
94
- Settings.PROXY_GEOSERVER_AUTH.gsub('Basic ', '')
95
- end
96
-
97
- # Tests if geoserver credentials are set beyond the default.
98
- def geoserver_credentials_valid?
99
- Settings.PROXY_GEOSERVER_AUTH != 'Basic base64encodedusername:password'
100
- end
101
-
102
- # Tests if local thumbnail method is configured
103
- def gblsi_thumbnail_field?
104
- Settings.GBLSI_THUMBNAIL_FIELD
105
- end
106
-
107
- def gblsi_thumbnail_uri
108
- if gblsi_thumbnail_field? && @document[Settings.GBLSI_THUMBNAIL_FIELD]
109
- @document[Settings.GBLSI_THUMBNAIL_FIELD]
110
- else
111
- false
112
- end
113
- end
114
-
115
- # Generates hash containing thumbnail mime_type and image.
116
- def image_data
117
- return nil unless image_url
118
-
119
- remote_image
120
- end
121
-
122
- # Gets thumbnail image from URL. On error, placehold image.
123
- def remote_image
124
- auth = geoserver_credentials
125
-
126
- uri = Addressable::URI.parse(image_url)
127
-
128
- return nil unless uri.scheme.include?('http')
129
-
130
- conn = Faraday.new(url: uri.normalize.to_s) do |b|
131
- b.use FaradayMiddleware::FollowRedirects
132
- b.adapter :net_http
133
- end
134
-
135
- conn.options.timeout = timeout
136
- conn.authorization :Basic, auth if auth
137
- conn.get.body
138
- rescue Faraday::Error::ConnectionFailed
139
- @metadata['error'] = 'Faraday::Error::ConnectionFailed'
140
- @metadata['placeheld'] = true
141
- nil
142
- rescue Faraday::Error::TimeoutError
143
- @metadata['error'] = 'Faraday::Error::TimeoutError'
144
- @metadata['placeheld'] = true
145
- nil
146
- end
147
-
148
- # Returns the thumbnail url.
149
- # If the layer is restriced Local WMS, and the geoserver credentials
150
- # have not been set beyond the default, then a thumbnail url from
151
- # dct references is used instead.
152
- def image_url
153
- @image_url ||= begin
154
- if gblsi_thumbnail_uri
155
- gblsi_thumbnail_uri
156
- elsif restricted_scanned_map?
157
- image_reference
158
- elsif restricted_wms_layer? && !geoserver_credentials_valid?
159
- image_reference
160
- else
161
- service_url || image_reference
162
- end
163
- end
164
- end
165
-
166
- # Checks if the document is Local restriced access and is a scanned map.
167
- def restricted_scanned_map?
168
- @document.local_restricted? && @document['layer_geom_type_s'] == 'Image'
169
- end
170
-
171
- # Checks if the document is Local restriced access and is a wms layer.
172
- def restricted_wms_layer?
173
- @document.local_restricted? && @document.viewer_protocol == 'wms'
174
- end
175
-
176
- # Gets the url for a specific service endpoint if the item is
177
- # public, has the same institution as the GBL instance, and the viewer
178
- # protocol is not 'map' or nil. A module name is then dynamically generated
179
- # from the viewer protocol, and if it's loaded, the image_url
180
- # method is called.
181
- def service_url
182
- @service_url ||=
183
- begin
184
- return unless @document.available?
185
-
186
- protocol = @document.viewer_protocol
187
- if protocol == 'map' || protocol.nil?
188
- @metadata['error'] = 'Unsupported viewer protocol'
189
- @metadata['placeheld'] = true
190
- return nil
191
- end
192
- "ImageService::#{protocol.camelcase}".constantize.image_url(@document, image_size)
193
- rescue NameError
194
- @metadata['error'] = 'service_url NameError'
195
- @metadata['placeheld'] = true
196
- return nil
197
- end
198
- end
199
-
200
- # Retreives a url to a static thumbnail from the document's dct_references field, if it exists.
201
- def image_reference
202
- return nil if @document[@document.references.reference_field].nil?
203
-
204
- JSON.parse(@document[@document.references.reference_field])['http://schema.org/thumbnailUrl']
205
- end
206
-
207
- # Default image size.
208
- def image_size
209
- 1500
210
- end
211
-
212
- # Faraday timeout value.
213
- def timeout
214
- 30
215
- end
216
-
217
- # Capture metadata within image harvest log
218
- def log_output
219
- @metadata['state'] = @document.sidecar.image_state.current_state
220
- @metadata.each do |key, value|
221
- @logger.tagged(@document.id, key.to_s) { @logger.info value }
222
- end
223
- end
224
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class ImageService
4
- module DynamicMapLayer
5
- ##
6
- # Formats and returns a thumbnail url from an ESRI Dynamic Map Layer endpoint.
7
- # @param [SolrDocument]
8
- # @return [String] image url
9
- def self.image_url(document, _size)
10
- "#{document.viewer_endpoint}/info/thumbnail/thumbnail.png"
11
- end
12
- end
13
- end
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class ImageService
4
- module Iiif
5
- ##
6
- # Formats and returns a thumbnail url from an International Image
7
- # Interoperability Framework endpoint.
8
- # @param [SolrDocument]
9
- # @param [Integer] thumbnail size
10
- # @return [String] iiif thumbnail url
11
- def self.image_url(document, size)
12
- "#{document.viewer_endpoint.gsub('info.json', '')}full/#{size},/0/default.jpg"
13
- end
14
- end
15
- end
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class ImageService
4
- module ImageMapLayer
5
- ##
6
- # Formats and returns a thumbnail url from an ESRI Image Map Layer endpoint.
7
- # information about the layer.
8
- # @param [SolrDocument]
9
- # @param [Integer] thumbnail size
10
- # @return [String] thumbnail url
11
- def self.image_url(document, _size)
12
- "#{document.viewer_endpoint}/info/thumbnail/thumbnail.png"
13
- end
14
- end
15
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class ImageService
4
- module TiledMapLayer
5
- ##
6
- # Formats and returns an image url from an ESRI Tiled Map Layer endpoint.
7
- # @param [SolrDocument]
8
- # @return [String] image url
9
- def self.image_url(document, _size)
10
- "#{document.viewer_endpoint}/info/thumbnail/thumbnail.png"
11
- end
12
- end
13
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class ImageService
4
- module Wms
5
- ##
6
- # Formats and returns a thumbnail url from a Web Map Service endpoint.
7
- # This utilizes the GeoServer specific 'reflect' service to generate
8
- # parameters like bbox that are difficult to tweak without more detailed
9
- # information about the layer.
10
- # @param [SolrDocument]
11
- # @param [Integer] thumbnail size
12
- # @return [String] wms thumbnail url
13
- def self.image_url(document, size)
14
- # Swap proxy url with princeton geoserver url.
15
- # Thumbnail requests send geoserver auth.
16
- endpoint = document.viewer_endpoint.gsub(Settings.PROXY_GEOSERVER_URL,
17
- Settings.INSTITUTION_GEOSERVER_URL)
18
- "#{endpoint}/reflect?" \
19
- '&FORMAT=image%2Fpng' \
20
- '&TRANSPARENT=TRUE' \
21
- "&LAYERS=#{document['layer_id_s']}" \
22
- "&WIDTH=#{size}" \
23
- "&HEIGHT=#{size}"
24
- end
25
- end
26
- end