iiif_print 1.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.
Files changed (211) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +2 -0
  3. data/.env +5 -0
  4. data/.fcrepo_wrapper +4 -0
  5. data/.github/release.yml +20 -0
  6. data/.github/workflows/branches.yml +24 -0
  7. data/.github/workflows/build-lint-test-action.yaml +33 -0
  8. data/.github/workflows/release_labels.yml +25 -0
  9. data/.gitignore +52 -0
  10. data/.rubocop.yml +177 -0
  11. data/.solr_wrapper +8 -0
  12. data/.travis.yml +49 -0
  13. data/CONTRIBUTING.md +181 -0
  14. data/Dockerfile +15 -0
  15. data/Gemfile +52 -0
  16. data/LICENSE +203 -0
  17. data/README.md +203 -0
  18. data/Rakefile +38 -0
  19. data/app/actors/iiif_print/actors/file_set_actor_decorator.rb +56 -0
  20. data/app/assets/config/iiif_print_manifest.js +2 -0
  21. data/app/assets/images/iiif_print/.keep +0 -0
  22. data/app/assets/javascripts/iiif_print/autocomplete_fix.js +33 -0
  23. data/app/assets/javascripts/iiif_print/ocr_search.js.erb +6 -0
  24. data/app/assets/javascripts/iiif_print.js +3 -0
  25. data/app/assets/stylesheets/iiif_print/_iiif_print.scss +4 -0
  26. data/app/assets/stylesheets/iiif_print/_issue_search.scss +13 -0
  27. data/app/assets/stylesheets/iiif_print/_issues_calendar.scss +18 -0
  28. data/app/assets/stylesheets/iiif_print/_newspapers_search.scss +38 -0
  29. data/app/assets/stylesheets/iiif_print/_search_results.scss +6 -0
  30. data/app/helpers/hyrax/iiif_helper.rb +22 -0
  31. data/app/helpers/iiif_print/application_helper.rb +5 -0
  32. data/app/helpers/iiif_print_helper.rb +64 -0
  33. data/app/indexers/concerns/iiif_print/child_indexer.rb +34 -0
  34. data/app/indexers/concerns/iiif_print/file_set_indexer.rb +29 -0
  35. data/app/mailers/iiif_print/application_mailer.rb +8 -0
  36. data/app/models/concerns/iiif_print/set_child_flag.rb +29 -0
  37. data/app/models/concerns/iiif_print/solr/document.rb +47 -0
  38. data/app/models/iiif_print/application_record.rb +6 -0
  39. data/app/models/iiif_print/derivative_attachment.rb +8 -0
  40. data/app/models/iiif_print/iiif_search_response_decorator.rb +17 -0
  41. data/app/models/iiif_print/ingest_file_relation.rb +14 -0
  42. data/app/models/iiif_print/pending_relationship.rb +7 -0
  43. data/app/presenters/iiif_print/iiif_manifest_presenter_behavior.rb +10 -0
  44. data/app/presenters/iiif_print/iiif_manifest_presenter_factory_behavior.rb +33 -0
  45. data/app/presenters/iiif_print/work_show_presenter_decorator.rb +29 -0
  46. data/app/renderers/hyrax/renderers/faceted_attribute_renderer_decorator.rb +18 -0
  47. data/app/search_builders/concerns/iiif_print/exclude_models.rb +17 -0
  48. data/app/search_builders/concerns/iiif_print/highlight_search_params.rb +14 -0
  49. data/app/services/iiif_print/manifest_builder_service_behavior.rb +97 -0
  50. data/app/services/iiif_print/pluggable_derivative_service.rb +120 -0
  51. data/app/views/catalog/_snippets_more.html.erb +16 -0
  52. data/app/views/hyrax/base/_representative_media.html.erb +9 -0
  53. data/app/views/hyrax/base/iiif_viewers/_universal_viewer.html.erb +8 -0
  54. data/app/views/hyrax/file_sets/_actions.html.erb +45 -0
  55. data/bin/rails +13 -0
  56. data/config/fcrepo_wrapper_test.yml +5 -0
  57. data/config/initializers/assets.rb +2 -0
  58. data/config/locales/iiif_print.de.yml +148 -0
  59. data/config/locales/iiif_print.en.yml +119 -0
  60. data/config/locales/iiif_print.es.yml +148 -0
  61. data/config/locales/iiif_print.fr.yml +149 -0
  62. data/config/locales/iiif_print.it.yml +142 -0
  63. data/config/locales/iiif_print.pt-BR.yml +148 -0
  64. data/config/locales/iiif_print.zh.yml +142 -0
  65. data/config/solr_wrapper_test.yml +9 -0
  66. data/config/test-fixture/solr-config/_rest_managed.json +3 -0
  67. data/config/test-fixture/solr-config/admin-extra.html +31 -0
  68. data/config/test-fixture/solr-config/elevate.xml +36 -0
  69. data/config/test-fixture/solr-config/mapping-ISOLatin1Accent.txt +246 -0
  70. data/config/test-fixture/solr-config/protwords.txt +21 -0
  71. data/config/test-fixture/solr-config/schema.xml +366 -0
  72. data/config/test-fixture/solr-config/scripts.conf +24 -0
  73. data/config/test-fixture/solr-config/solrconfig.xml +322 -0
  74. data/config/test-fixture/solr-config/spellings.txt +2 -0
  75. data/config/test-fixture/solr-config/stopwords.txt +58 -0
  76. data/config/test-fixture/solr-config/stopwords_en.txt +58 -0
  77. data/config/test-fixture/solr-config/synonyms.txt +31 -0
  78. data/config/test-fixture/solr-config/xslt/example.xsl +132 -0
  79. data/config/test-fixture/solr-config/xslt/example_atom.xsl +67 -0
  80. data/config/test-fixture/solr-config/xslt/example_rss.xsl +66 -0
  81. data/config/test-fixture/solr-config/xslt/luke.xsl +337 -0
  82. data/config/vendor/fits.xml +55 -0
  83. data/config/vendor/imagemagick-6-policy.xml +76 -0
  84. data/db/migrate/20181214181358_create_iiif_print_derivative_attachments.rb +12 -0
  85. data/db/migrate/20190107165909_create_iiif_print_ingest_file_relations.rb +11 -0
  86. data/db/migrate/20230109000000_create_iiif_print_pending_relationships.rb +11 -0
  87. data/docker-compose.yml +129 -0
  88. data/iiif_print.gemspec +43 -0
  89. data/lib/generators/iiif_print/assets_generator.rb +29 -0
  90. data/lib/generators/iiif_print/catalog_controller_generator.rb +32 -0
  91. data/lib/generators/iiif_print/install_generator.rb +52 -0
  92. data/lib/generators/iiif_print/templates/config/initializers/iiif_print.rb +22 -0
  93. data/lib/generators/iiif_print/templates/iiif_print.scss +1 -0
  94. data/lib/iiif_print/base_derivative_service.rb +113 -0
  95. data/lib/iiif_print/blacklight_iiif_search/annotation_decorator.rb +84 -0
  96. data/lib/iiif_print/catalog_search_builder.rb +31 -0
  97. data/lib/iiif_print/configuration.rb +99 -0
  98. data/lib/iiif_print/data/fileset_helper.rb +25 -0
  99. data/lib/iiif_print/data/path_helper.rb +40 -0
  100. data/lib/iiif_print/data/work_derivatives.rb +323 -0
  101. data/lib/iiif_print/data/work_file.rb +92 -0
  102. data/lib/iiif_print/data/work_files.rb +199 -0
  103. data/lib/iiif_print/data.rb +35 -0
  104. data/lib/iiif_print/engine.rb +77 -0
  105. data/lib/iiif_print/errors.rb +9 -0
  106. data/lib/iiif_print/image_tool.rb +119 -0
  107. data/lib/iiif_print/jobs/application_job.rb +8 -0
  108. data/lib/iiif_print/jobs/child_works_from_pdf_job.rb +107 -0
  109. data/lib/iiif_print/jobs/create_relationships_job.rb +78 -0
  110. data/lib/iiif_print/jp2_derivative_service.rb +118 -0
  111. data/lib/iiif_print/jp2_image_metadata.rb +81 -0
  112. data/lib/iiif_print/lineage_service.rb +41 -0
  113. data/lib/iiif_print/metadata.rb +125 -0
  114. data/lib/iiif_print/pdf_derivative_service.rb +42 -0
  115. data/lib/iiif_print/split_pdfs/child_work_creation_from_pdf_service.rb +75 -0
  116. data/lib/iiif_print/split_pdfs/pages_into_images_service.rb +130 -0
  117. data/lib/iiif_print/split_pdfs/pdf_image_extraction_service.rb +85 -0
  118. data/lib/iiif_print/text_extraction/alto_reader.rb +123 -0
  119. data/lib/iiif_print/text_extraction/hocr_reader.rb +172 -0
  120. data/lib/iiif_print/text_extraction/page_ocr.rb +87 -0
  121. data/lib/iiif_print/text_extraction/render_alto.rb +84 -0
  122. data/lib/iiif_print/text_extraction/word_coords_builder.rb +38 -0
  123. data/lib/iiif_print/text_extraction.rb +11 -0
  124. data/lib/iiif_print/text_extraction_derivative_service.rb +47 -0
  125. data/lib/iiif_print/text_formats_from_alto_service.rb +77 -0
  126. data/lib/iiif_print/tiff_derivative_service.rb +50 -0
  127. data/lib/iiif_print/version.rb +3 -0
  128. data/lib/iiif_print/works_controller_behavior.rb +9 -0
  129. data/lib/iiif_print.rb +136 -0
  130. data/lib/tasks/set_child_works.rake +22 -0
  131. data/spec/.keep.txt +1 -0
  132. data/spec/factories/ability.rb +6 -0
  133. data/spec/factories/newspaper_issue.rb +7 -0
  134. data/spec/factories/newspaper_page.rb +7 -0
  135. data/spec/factories/newspaper_page_solr_document.rb +12 -0
  136. data/spec/factories/newspaper_title.rb +8 -0
  137. data/spec/factories/uploaded_pdf_file.rb +9 -0
  138. data/spec/factories/uploaded_txt_file.rb +9 -0
  139. data/spec/factories/user.rb +13 -0
  140. data/spec/fixtures/files/4.1.07.jp2 +0 -0
  141. data/spec/fixtures/files/4.1.07.tiff +0 -0
  142. data/spec/fixtures/files/README.md +7 -0
  143. data/spec/fixtures/files/alto-2-0.xsd +714 -0
  144. data/spec/fixtures/files/broken-truncated.pdf +0 -0
  145. data/spec/fixtures/files/credits.md +16 -0
  146. data/spec/fixtures/files/lowres-gray-via-ndnp-sample.tiff +0 -0
  147. data/spec/fixtures/files/minimal-1-page.pdf +0 -0
  148. data/spec/fixtures/files/minimal-2-page.pdf +0 -0
  149. data/spec/fixtures/files/minimal-alto.xml +31 -0
  150. data/spec/fixtures/files/ndnp-alto-sample.xml +24 -0
  151. data/spec/fixtures/files/ndnp-sample1-json.json +1 -0
  152. data/spec/fixtures/files/ndnp-sample1-txt.txt +1 -0
  153. data/spec/fixtures/files/ndnp-sample1.pdf +0 -0
  154. data/spec/fixtures/files/ocr_alto.xml +202 -0
  155. data/spec/fixtures/files/ocr_alto_scaled_4pts_per_px.xml +202 -0
  156. data/spec/fixtures/files/ocr_color.tiff +0 -0
  157. data/spec/fixtures/files/ocr_gray.jp2 +0 -0
  158. data/spec/fixtures/files/ocr_gray.tiff +0 -0
  159. data/spec/fixtures/files/ocr_mono.tiff +0 -0
  160. data/spec/fixtures/files/ocr_mono_text_hocr.html +78 -0
  161. data/spec/fixtures/files/page1.tiff +0 -0
  162. data/spec/fixtures/files/sample-4page-issue.pdf +0 -0
  163. data/spec/fixtures/files/sample-color-newsletter.pdf +0 -0
  164. data/spec/fixtures/files/thumbnail.jpg +0 -0
  165. data/spec/helpers/hyrax/iiif_helper_spec.rb +65 -0
  166. data/spec/helpers/iiif_print_helper_spec.rb +43 -0
  167. data/spec/iiif_print/base_derivative_service_spec.rb +11 -0
  168. data/spec/iiif_print/blacklight_iiif_search/annotation_decorator_spec.rb +51 -0
  169. data/spec/iiif_print/catalog_search_builder_spec.rb +60 -0
  170. data/spec/iiif_print/configuration_spec.rb +67 -0
  171. data/spec/iiif_print/data/work_derivatives_spec.rb +245 -0
  172. data/spec/iiif_print/data/work_file_spec.rb +99 -0
  173. data/spec/iiif_print/data/work_files_spec.rb +237 -0
  174. data/spec/iiif_print/image_tool_spec.rb +109 -0
  175. data/spec/iiif_print/jobs/child_works_from_pdf_job_spec.rb +30 -0
  176. data/spec/iiif_print/jobs/create_relationships_job_spec.rb +17 -0
  177. data/spec/iiif_print/jp2_image_metadata_spec.rb +37 -0
  178. data/spec/iiif_print/lineage_service_spec.rb +13 -0
  179. data/spec/iiif_print/metadata_spec.rb +115 -0
  180. data/spec/iiif_print/split_pdfs/pages_into_images_service_spec.rb +6 -0
  181. data/spec/iiif_print/text_extraction/alto_reader_spec.rb +49 -0
  182. data/spec/iiif_print/text_extraction/hocr_reader_spec.rb +45 -0
  183. data/spec/iiif_print/text_extraction/page_ocr_spec.rb +84 -0
  184. data/spec/iiif_print/text_extraction/render_alto_spec.rb +54 -0
  185. data/spec/iiif_print/text_extraction/word_coords_builder_spec.rb +44 -0
  186. data/spec/iiif_print_spec.rb +51 -0
  187. data/spec/misc_shared.rb +111 -0
  188. data/spec/models/iiif_print/derivative_attachment_spec.rb +37 -0
  189. data/spec/models/iiif_print/ingest_file_relation_spec.rb +56 -0
  190. data/spec/models/solr_document_spec.rb +14 -0
  191. data/spec/presenters/iiif_print/iiif_manifest_presenter_behavior_spec.rb +19 -0
  192. data/spec/presenters/iiif_print/iiif_manifest_presenter_factory_behavior_spec.rb +49 -0
  193. data/spec/services/iiif_print/jp2_derivative_service_spec.rb +59 -0
  194. data/spec/services/iiif_print/pdf_derivative_service_spec.rb +66 -0
  195. data/spec/services/iiif_print/pluggable_derivative_service_spec.rb +178 -0
  196. data/spec/services/iiif_print/text_extraction_derivative_service_spec.rb +82 -0
  197. data/spec/services/iiif_print/text_formats_from_alto_service_spec.rb +127 -0
  198. data/spec/services/iiif_print/tiff_derivative_service_spec.rb +65 -0
  199. data/spec/spec_helper.rb +181 -0
  200. data/spec/support/controller_level_helpers.rb +28 -0
  201. data/spec/support/iiif_print_models.rb +127 -0
  202. data/spec/test_app_templates/blacklight.yml +9 -0
  203. data/spec/test_app_templates/fedora.yml +15 -0
  204. data/spec/test_app_templates/lib/generators/test_app_generator.rb +40 -0
  205. data/spec/test_app_templates/redis.yml +9 -0
  206. data/spec/test_app_templates/solr/conf/schema.xml +362 -0
  207. data/spec/test_app_templates/solr/conf/solrconfig.xml +322 -0
  208. data/spec/test_app_templates/solr.yml +7 -0
  209. data/tasks/iiif_print_dev.rake +34 -0
  210. data/tmp/.keep +0 -0
  211. metadata +605 -0
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
3
+
4
+ # @note This spec was brought over from Hyrax 3.4.2
5
+ # @see https://github.com/samvera/hyrax/blob/afdda8240494ed382301f7d0ab0fd7bafe79185e/spec/helpers/hyrax/iiif_helper_spec.rb
6
+ RSpec.describe Hyrax::IiifHelper, type: :helper do
7
+ let(:solr_document) { SolrDocument.new }
8
+ let(:request) { double }
9
+ let(:ability) { nil }
10
+ let(:presenter) { Hyrax::WorkShowPresenter.new(solr_document, ability, request) }
11
+ let(:uv_partial_path) { 'hyrax/base/iiif_viewers/universal_viewer' }
12
+
13
+ describe '#iiif_viewer_display' do
14
+ before do
15
+ allow(helper).to receive(:iiif_viewer_display_partial).with(presenter)
16
+ .and_return(uv_partial_path)
17
+ end
18
+
19
+ it "renders a partial" do
20
+ expect(helper).to receive(:render)
21
+ .with(uv_partial_path, presenter: presenter)
22
+ helper.iiif_viewer_display(presenter)
23
+ end
24
+
25
+ it "takes options" do
26
+ expect(helper).to receive(:render)
27
+ .with(uv_partial_path, presenter: presenter, transcript_id: '123')
28
+ helper.iiif_viewer_display(presenter, transcript_id: '123')
29
+ end
30
+ end
31
+
32
+ describe '#iiif_viewer_display_partial' do
33
+ subject { helper.iiif_viewer_display_partial(presenter) }
34
+
35
+ it 'defaults to universal viewer' do
36
+ expect(subject).to eq uv_partial_path
37
+ end
38
+
39
+ context "with #iiif_viewer override" do
40
+ let(:iiif_viewer) { :mirador }
41
+
42
+ before do
43
+ allow(presenter).to receive(:iiif_viewer).and_return(iiif_viewer)
44
+ end
45
+
46
+ it { is_expected.to eq 'hyrax/base/iiif_viewers/mirador' }
47
+ end
48
+ end
49
+
50
+ describe '#universal_viewer_base_url' do
51
+ subject { helper.universal_viewer_base_url }
52
+
53
+ it 'defaults to universal viewer base path' do
54
+ expect(subject).to eq "http://test.host/uv/uv.html"
55
+ end
56
+ end
57
+
58
+ describe '#universal_viewer_config_url' do
59
+ subject { helper.universal_viewer_config_url }
60
+
61
+ it 'defaults to universal viewer base path' do
62
+ expect(subject).to eq "http://test.host/uv/uv-config.json"
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe IiifPrintHelper do
4
+ let(:query_term) { 'suffrage' }
5
+ let(:query_params_hash) { { q: query_term } }
6
+ let(:document) { build(:newspaper_page_solr_document) }
7
+
8
+ describe '#iiif_search_anchor' do
9
+ it 'returns the correct string' do
10
+ expect(helper.iiif_search_anchor({})).to eq nil
11
+ expect(helper.iiif_search_anchor(query_params_hash)).to eq("?h=#{query_term}")
12
+ end
13
+ end
14
+
15
+ describe '#search_query' do
16
+ it 'returns the correct string' do
17
+ expect(helper.search_query({})).to eq nil
18
+ expect(helper.search_query(query_params_hash)).to eq(query_term)
19
+ end
20
+ end
21
+
22
+ describe '#highlight_matches' do
23
+ let(:hl_fl) { 'all_text_tsimv' }
24
+
25
+ describe 'when highlighting is present in Solr response' do
26
+ before do
27
+ allow(document).to receive(:highlight_field).with(hl_fl).and_return(['foo <em>bar</em> baz'.html_safe])
28
+ end
29
+ it 'returns the matching terms when highlighting present' do
30
+ expect(helper.highlight_matches(document, hl_fl, 'em')).to eq 'bar'
31
+ end
32
+ end
33
+
34
+ describe 'when highlighting is not present' do
35
+ before do
36
+ allow(document).to receive(:highlight_field).with(hl_fl).and_return([])
37
+ end
38
+ it 'returns the matching terms when highlighting present' do
39
+ expect(helper.highlight_matches(document, hl_fl, 'em')).to eq nil
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe IiifPrint::BaseDerivativeService do
4
+ describe '#valid?' do
5
+ let(:file_set) { double(FileSet) }
6
+ let(:service) { described_class.new(file_set) }
7
+ subject { service.valid? }
8
+
9
+ it { is_expected.to be_truthy }
10
+ end
11
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe IiifPrint::BlacklightIiifSearch::AnnotationDecorator do
4
+ let(:page_document) { build(:newspaper_page_solr_document) }
5
+ let(:controller) { CatalogController.new }
6
+ let(:coordinates) do
7
+ JSON.parse("{\"coords\":{\"software\":[[2641,4102,512,44]]}}")
8
+ end
9
+ let(:parent_document) do
10
+ SolrDocument.new('id' => page_document[:issue_id_ssi],
11
+ 'has_model_ssim' => ['NewspaperIssue'])
12
+ end
13
+ let(:iiif_search_annotation) do
14
+ BlacklightIiifSearch::IiifSearchAnnotation.new(page_document, 'software',
15
+ 0, nil, controller,
16
+ parent_document)
17
+ end
18
+ let(:test_request) { ActionDispatch::TestRequest.new({}) }
19
+
20
+ before do
21
+ allow(controller).to receive(:request).and_return(test_request)
22
+ allow(controller).to receive(:polymorphic_url)
23
+ .with(parent_document, host: test_request.base_url, locale: nil)
24
+ .and_return("/#{page_document[:issue_id_ssi]}")
25
+ end
26
+
27
+ describe '#annotation_id' do
28
+ subject { iiif_search_annotation.annotation_id }
29
+ it 'returns a properly formatted URL' do
30
+ expect(subject).to include("#{page_document[:issue_id_ssi]}/manifest/canvas/#{page_document[:file_set_ids_ssim].first}/annotation/0")
31
+ end
32
+ end
33
+
34
+ describe '#canvas_uri_for_annotation' do
35
+ before { allow(iiif_search_annotation).to receive(:fetch_and_parse_coords).and_return(coordinates) }
36
+
37
+ subject { iiif_search_annotation.canvas_uri_for_annotation }
38
+ it 'returns a properly formatted URL' do
39
+ expect(subject).to include("#{page_document[:issue_id_ssi]}/manifest/canvas/#{page_document[:file_set_ids_ssim].first}")
40
+ end
41
+
42
+ describe 'private methods' do
43
+ # test #coordinates based on output of #canvas_uri_for_annotation, which calls it
44
+ describe '#coordinates' do
45
+ it 'gets the expected value from #coordinates' do
46
+ expect(subject).to include("#xywh=2641,4102,512,44")
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe IiifPrint::CatalogSearchBuilder do
4
+ # specs for IiifPrint::HighlightSearchParams
5
+ describe 'highlight_search_params' do
6
+ let(:solr_parameters) { { q: 'abolition' } }
7
+ subject { described_class.new(solr_parameters) }
8
+
9
+ it 'is included in the default_processor_chain' do
10
+ expect(described_class.default_processor_chain).to include(:highlight_search_params)
11
+ end
12
+
13
+ before { subject.highlight_search_params(solr_parameters) }
14
+ it 'adds the highlight fields to solr_parameters' do
15
+ expect(solr_parameters[:hl]).to be_truthy
16
+ expect(solr_parameters[:'hl.fl']).to eq('all_text_tsimv')
17
+ end
18
+ end
19
+
20
+ # specs for IiifPrint::ExcludeModels
21
+ describe 'exclude_models' do
22
+ let(:solr_parameters) { { all_fields: 'prohibition' } }
23
+ subject { described_class.new(solr_parameters) }
24
+
25
+ it 'is included in the default_processor_chain' do
26
+ expect(described_class.default_processor_chain).to include(:exclude_models)
27
+ end
28
+
29
+ context 'with configured model name solr field values' do
30
+ before do
31
+ config = IiifPrint::Configuration.new.tap { |c| c.excluded_model_name_solr_field_values = ['Excluded Model', 'Another Excluded Model'] }
32
+ subject.exclude_models(solr_parameters, config: config)
33
+ end
34
+
35
+ it 'adds the facet fields to solr_parameters with default key' do
36
+ expect(solr_parameters[:fq]).to be_truthy
37
+ expect(solr_parameters[:fq]).to(
38
+ include("-human_readable_type_sim:\"Excluded Model\"", "-human_readable_type_sim:\"Another Excluded Model\"")
39
+ )
40
+ end
41
+
42
+ context 'with configured model name solr field key' do
43
+ before do
44
+ config = IiifPrint::Configuration.new.tap do |c|
45
+ c.excluded_model_name_solr_field_values = ['ExcludedModel', 'AnotherExcludedModel']
46
+ c.excluded_model_name_solr_field_key = 'has_model_ssim'
47
+ end
48
+ subject.exclude_models(solr_parameters, config: config)
49
+ end
50
+
51
+ it 'adds the facet fields to solr_parameters with configured key' do
52
+ expect(solr_parameters[:fq]).to be_truthy
53
+ expect(solr_parameters[:fq]).to(
54
+ include("-has_model_ssim:\"ExcludedModel\"", "-has_model_ssim:\"AnotherExcludedModel\"")
55
+ )
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe IiifPrint::Configuration do
4
+ let(:config) { described_class.new }
5
+
6
+ describe "#metadata_fields" do
7
+ subject { config.metadata_fields }
8
+
9
+ it { is_expected.to be_a Hash }
10
+ it "allows for an override" do
11
+ original = config.metadata_fields
12
+ config.metadata_fields = { title: {} }
13
+ expect(config.metadata_fields).not_to eq original
14
+ end
15
+ end
16
+
17
+ describe "#sort_iiif_manifest_canvases_by" do
18
+ subject { config.sort_iiif_manifest_canvases_by }
19
+
20
+ it { is_expected.to be_a Symbol }
21
+ it "allows for an override" do
22
+ original = config.sort_iiif_manifest_canvases_by
23
+ config.sort_iiif_manifest_canvases_by = :title
24
+ expect(config.metadata_fields).not_to eq original
25
+ end
26
+ end
27
+
28
+ describe "#handle_after_create_fileset" do
29
+ let(:file_set) { double(FileSet) }
30
+ let(:user) { double(User) }
31
+ subject(:called_function) { config.handle_after_create_fileset(file_set, user) }
32
+
33
+ context "without configuration" do
34
+ it "calls IiifPrint::Data.handle_after_create_fileset" do
35
+ expect(IiifPrint::Data).to receive(:handle_after_create_fileset).with(file_set, user)
36
+
37
+ called_function
38
+ end
39
+ end
40
+
41
+ context "with configuration" do
42
+ let(:config_func) { ->(_file_set, _user) { :yup } }
43
+
44
+ it "calls the given configured lambda" do
45
+ config.after_create_fileset_handler = config_func
46
+ expect(IiifPrint::Data).not_to receive(:handle_after_create_fileset)
47
+ expect(config_func).to receive(:call).with(file_set, user)
48
+ called_function
49
+ end
50
+ end
51
+ end
52
+
53
+ describe '#additional_tessearct_options' do
54
+ context "by default" do
55
+ subject { config.additional_tessearct_options }
56
+ it { is_expected.not_to be_present }
57
+ end
58
+
59
+ it "can be configured" do
60
+ expect do
61
+ config.additional_tessearct_options = "-l esperanto"
62
+ end.to change(config, :additional_tessearct_options)
63
+ .from("")
64
+ .to("-l esperanto")
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,245 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+ require 'misc_shared'
5
+
6
+ RSpec.describe IiifPrint::Data::WorkDerivatives do
7
+ include_context "shared setup"
8
+
9
+ let(:bare_work) do
10
+ work = NewspaperPage.new
11
+ work.title = ['Another one']
12
+ work.save!
13
+ work
14
+ end
15
+
16
+ let(:work) do
17
+ # sample work comes from shared setup, but we need derivative, save...
18
+ mk_txt_derivative(sample_work)
19
+ sample_work.save!(validate: false)
20
+ sample_work
21
+ end
22
+
23
+ let(:adapter) { described_class.new(work) }
24
+
25
+ let(:txt1) do
26
+ registered_dirs = IiifPrint.config.registered_ingest_dirs
27
+ registered_dirs.push('/tmp') unless registered_dirs.include?('/tmp')
28
+ file = Tempfile.new(['txt1', '.txt'])
29
+ file.write('hello')
30
+ file.flush
31
+ file
32
+ end
33
+
34
+ let(:txt2) do
35
+ file = Tempfile.new('txt2.txt')
36
+ file.write('bye')
37
+ file.flush
38
+ file
39
+ end
40
+
41
+ let(:encoded_text) do
42
+ file = Tempfile.new('txt_encoded.txt', encoding: 'UTF-8')
43
+ file.write('Gorgonzola Dolce® — on sale for £12.50/kg')
44
+ file.flush
45
+ file
46
+ end
47
+
48
+ describe "enumerates available derivatives like hash" do
49
+ xit "includes expected derivative path for work" do
50
+ expect(adapter.keys).to include 'txt'
51
+ end
52
+
53
+ xit "can be introspected for quantity of derivatives" do
54
+ # `size` method without argument is count of derivatives,
55
+ # functions equivalently to adapter.keys.size
56
+ expect(adapter.size).to eq adapter.keys.size
57
+ end
58
+
59
+ xit "enumerates expected derivative extension for work" do
60
+ ext_found = adapter.keys
61
+ expect(ext_found).to include 'txt'
62
+ end
63
+
64
+ xit "enumerates expected derivative extension for file set" do
65
+ file_set = work.members.detect { |m| m.is_a? FileSet }
66
+ adapter = described_class.new(file_set)
67
+ ext_found = adapter.keys
68
+ expect(ext_found).to include 'txt'
69
+ end
70
+
71
+ xit "enumerates expected derivative extension for file set id" do
72
+ file_set = work.members.detect { |m| m.is_a? FileSet }
73
+ adapter = described_class.new(file_set.id)
74
+ ext_found = adapter.keys
75
+ expect(ext_found).to include 'txt'
76
+ end
77
+ end
78
+
79
+ describe "loads derivatives for a work" do
80
+ xit "Loads text derivative path" do
81
+ expect(File.exist?(adapter.path('txt'))).to be true
82
+ expect(adapter.exist?('txt')).to be true
83
+ end
84
+
85
+ xit "Loads text derivative data" do
86
+ expect(adapter.data('txt')).to include 'mythical'
87
+ end
88
+
89
+ xit "Handles character encoding on read" do
90
+ # replace fixture text derivative for work with encoded text
91
+ adapter.attach(encoded_text.path, 'txt')
92
+ data = adapter.data('txt')
93
+ expect(data).to include '—' # em-dash
94
+ expect(data).to include '£' # gb-pound sign
95
+ expect(data.encoding.to_s).to eq 'UTF-8'
96
+ end
97
+
98
+ xit "Loads thumbnail derivative data" do
99
+ mk_thumbnail_derivative(work)
100
+ # get size by loading data
101
+ expect(adapter.data('thumbnail').bytes.size).to eq 16_743
102
+ # get size by File.size via .size method
103
+ expect(adapter.size('thumbnail')).to eq 16_743
104
+ end
105
+
106
+ xit "Can access jp2 derivative" do
107
+ mk_jp2_derivative(work)
108
+ expect(File.exist?(adapter.path('jp2'))).to be true
109
+ expect(adapter.exist?('jp2')).to be true
110
+ end
111
+ end
112
+
113
+ describe "create, update, delete derivatives" do
114
+ xit "will queue derivative file assignment" do
115
+ adapter = described_class.new(bare_work)
116
+ adapter.assign(example_gray_jp2)
117
+ expect(adapter.assigned).to include example_gray_jp2
118
+ end
119
+
120
+ xit "will fail to assign file in non-registered dirs" do
121
+ adapter = described_class.new(bare_work)
122
+ # need a non-whitlisted file that exists:
123
+ bad_path = File.expand_path("../../spec_helper.rb", fixture_path)
124
+ expect { adapter.assign(bad_path) }.to raise_error(SecurityError)
125
+ end
126
+
127
+ xit "will remove file assignment from queue" do
128
+ adapter = described_class.new(bare_work)
129
+ expect(adapter.state).to eq 'empty'
130
+ adapter.assign(example_gray_jp2)
131
+ expect(adapter.assigned).to include example_gray_jp2
132
+ expect(adapter.state).to eq 'dirty'
133
+ adapter.unassign(example_gray_jp2)
134
+ expect(adapter.assigned).not_to include example_gray_jp2
135
+ expect(adapter.state).to eq 'empty'
136
+ end
137
+
138
+ xit "will queue a deletion" do
139
+ # Given a work with a derivative (txt) already assigned
140
+ expect(adapter.state).to eq 'saved'
141
+ # unassigning path...
142
+ adapter.unassign('txt')
143
+ # will lead to queued unassignment (intent to delete)...
144
+ expect(adapter.unassigned).to include 'txt'
145
+ # and a 'dirty' adapter state (unflushed changes):
146
+ expect(adapter.state).to eq 'dirty'
147
+ end
148
+
149
+ xit "will flush a removal and addition on commit!" do
150
+ # Given a work with a derivative (txt) already assigned
151
+ expect(adapter.keys).to include 'txt'
152
+ expect(adapter.keys).not_to include 'jp2'
153
+ # unassigning path...
154
+ adapter.unassign('txt')
155
+ # and assigning another attachment:
156
+ adapter.assign(example_gray_jp2)
157
+ # ...committing these will flush the changes (synchronously):
158
+ adapter.commit!
159
+ expect(adapter.keys).not_to include 'txt'
160
+ expect(adapter.keys).to include 'jp2'
161
+ expect(adapter.size('jp2')).to eq 27_703
162
+ end
163
+
164
+ xit "can attach derivative from file" do
165
+ expect(adapter.keys).not_to include 'jp2'
166
+ adapter.attach(example_gray_jp2, 'jp2')
167
+ expect(adapter.exist?('jp2')).to be true
168
+ expect(adapter.path('jp2')).not_to be nil
169
+ expect(File.size(adapter.path('jp2'))).to eq File.size(example_gray_jp2)
170
+ expect(adapter.keys).to include 'jp2'
171
+ d_path = path_factory.derivative_path_for_reference(adapter.fileset_id, 'jp2')
172
+ expect(adapter.values).to include d_path
173
+ end
174
+
175
+ xit "can replace aderivative with new attachment" do
176
+ adapter.attach(txt1.path, 'txt')
177
+ expect(adapter.data('txt')).to eq 'hello'
178
+ adapter.attach(txt2.path, 'txt')
179
+ expect(adapter.data('txt')).to eq 'bye'
180
+ end
181
+
182
+ xit "can delete an attached derivative" do
183
+ adapter.attach(txt1.path, 'txt')
184
+ expect(adapter.keys).to include 'txt'
185
+ expect(adapter.data('txt')).to eq 'hello'
186
+ adapter.delete('txt')
187
+ expect(adapter.path('txt')).to be nil
188
+ expect(adapter.keys).not_to include 'txt'
189
+ end
190
+
191
+ xit "persists log of attachment to RDBMS" do
192
+ adapter.assign(txt1.path)
193
+ result = IiifPrint::DerivativeAttachment.find_by(
194
+ fileset_id: adapter.fileset.id,
195
+ path: txt1.path,
196
+ destination_name: 'txt'
197
+ )
198
+ expect(result).not_to be_nil
199
+ end
200
+
201
+ xit "persists a log of path relation to primary file" do
202
+ # this is an integration test by practical necessity, with
203
+ # WorkFiles adapting a bare work with no fileset.
204
+ work_files = IiifPrint::Data::WorkFiles.of(bare_work)
205
+ work_files.assign(example_gray_jp2)
206
+ adapter = work_files.derivatives
207
+ adapter.assign(txt1.path)
208
+ result = IiifPrint::IngestFileRelation.find_by(
209
+ derivative_path: txt1.path,
210
+ file_path: example_gray_jp2
211
+ )
212
+ expect(result).not_to be_nil
213
+ end
214
+
215
+ xit "commits queued derivatives" do
216
+ IiifPrint::IngestFileRelation.where(file_path: example_gray_jp2).delete_all
217
+ work_files = IiifPrint::Data::WorkFiles.of(bare_work)
218
+ work_files.assign(example_gray_jp2)
219
+ adapter = work_files.derivatives
220
+ adapter.assign(txt1.path)
221
+ expect(File.exist?(txt1.path)).to be true
222
+ expect(adapter.keys.size).to eq 0
223
+ # we need a fileset, saved with import_url, attached to work:
224
+ fileset = valid_file_set
225
+ fileset.import_url = 'file://' + example_gray_jp2
226
+ fileset.save!
227
+ bare_work.members.push(fileset)
228
+ bare_work.save!
229
+ fileset.reload
230
+ expect(fileset.member_of[0].id).to eq bare_work.id
231
+ # with a new adapter instance...
232
+ adapter2 = described_class.of(bare_work)
233
+ # call .commit_queued! with our fileset...
234
+ expect(File.exist?(txt1.path)).to be true
235
+ adapter2.commit_queued!(fileset)
236
+ # ...which should result in saved, reloaded derivative...
237
+ expect(adapter2.keys.size).to eq 1
238
+ expect(File.size(adapter2.values[0])).to eq File.size(txt1.path)
239
+ # ...also found via Hyrax::DerviativePath:
240
+ found = Hyrax::DerivativePath.derivatives_for_reference(fileset.id)
241
+ expect(found.size).to eq 1
242
+ expect(File.size(found[0])).to eq File.size(txt1.path)
243
+ end
244
+ end
245
+ end
@@ -0,0 +1,99 @@
1
+ require 'spec_helper'
2
+ require 'misc_shared'
3
+
4
+ RSpec.describe IiifPrint::Data::WorkFile do
5
+ include_context "shared setup"
6
+
7
+ # sample objects:
8
+ let(:work) { work_with_file }
9
+
10
+ describe "adapter composition" do
11
+ it "adapts work with nil fileset" do
12
+ adapter = described_class.new(work)
13
+ expect(adapter.work).to be work
14
+ expect(adapter.fileset).to be_nil
15
+ end
16
+
17
+ it "adapts work with 'of' alt constructor" do
18
+ adapter = described_class.of(work)
19
+ expect(adapter.work).to be work
20
+ end
21
+
22
+ it "adapts work and explicitly provided fileset" do
23
+ fileset = work.members.detect { |m| m.is_a? FileSet }
24
+ adapter = described_class.of(work, fileset)
25
+ expect(adapter.work).to be work
26
+ expect(adapter.fileset).to be fileset
27
+ end
28
+
29
+ it "constructs with a parent object, if provided" do
30
+ fileset = work.members.detect { |m| m.is_a? FileSet }
31
+ parent = double('parent')
32
+ adapter = described_class.of(work, fileset, parent)
33
+ expect(adapter.parent).to be parent
34
+ end
35
+ end
36
+
37
+ describe "read file metadata" do
38
+ it "gets original filename" do
39
+ fileset = work.members.detect { |m| m.is_a? FileSet }
40
+ adapter = described_class.of(work, fileset)
41
+ expect(adapter.name).to eq fileset.original_file.original_name
42
+ expect(adapter.name).to eq 'credits.md'
43
+ end
44
+
45
+ it "gets miscellaneous metadata field values" do
46
+ fileset = work.members.detect { |m| m.is_a? FileSet }
47
+ adapter = described_class.of(work, fileset)
48
+ # expectations for accessors of size, date_*, mime_type
49
+ expect(adapter.size).to eq File.size(txt_path)
50
+ expect(adapter.name).to eq 'credits.md'
51
+ expect(adapter.mime_type).to eq 'text/plain'
52
+ # getting actual value for date fields requires digging through
53
+ # multiple layers of ActiveTuples indirection...
54
+ expect(adapter.date_created.to_a[0].to_s).to eq static_date.to_s
55
+ expect(adapter.date_modified.to_a[0].to_s).to eq static_date.to_s
56
+ end
57
+ end
58
+
59
+ describe "read binary via transparent repository checkout" do
60
+ it "gets path (from checkout)" do
61
+ fileset = work.members.detect { |m| m.is_a? FileSet }
62
+ adapter = described_class.of(work, fileset)
63
+ # Get a path to a working copy
64
+ path = adapter.path
65
+ expect(path).to be_a String
66
+ expect(File.exist?(path)).to be true
67
+ # size of working copy binary checkout matches size in computed metadata
68
+ expect(File.size(path)).to eq fileset.original_file.size
69
+ end
70
+
71
+ it "gets data as bytes" do
72
+ fileset = work.members.detect { |m| m.is_a? FileSet }
73
+ adapter = described_class.of(work, fileset)
74
+ # Get a data from the working copy
75
+ data = adapter.data
76
+ expect(data).to be_a String
77
+ # size of working copy binary checkout matches size in computed metadata
78
+ expect(data.size).to eq fileset.original_file.size
79
+ end
80
+
81
+ it "runs block on data as IO" do
82
+ fileset = work.members.detect { |m| m.is_a? FileSet }
83
+ adapter = described_class.of(work, fileset)
84
+ adapter.with_io { |io| expect(io.read.size).to eq File.size(txt_path) }
85
+ end
86
+ end
87
+
88
+ describe "derivative access" do
89
+ it "gets derivatives for file" do
90
+ fileset = work.members.detect { |m| m.is_a? FileSet }
91
+ adapter = described_class.of(work, fileset)
92
+ expect(adapter.derivatives.class).to eq \
93
+ IiifPrint::Data::WorkDerivatives
94
+ expect(adapter.derivatives.fileset).to be fileset
95
+ expect(adapter.derivatives.work).to be work
96
+ expect(adapter.derivatives.parent).to be adapter
97
+ end
98
+ end
99
+ end