hyrax 5.0.1 → 5.0.3
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/.circleci/config.yml +7 -176
- data/.dassie/.env +8 -3
- data/.dassie/Gemfile +13 -2
- data/.dassie/app/controllers/hyrax/generic_work_resources_controller.rb +17 -0
- data/.dassie/app/controllers/hyrax/generic_works_controller.rb +7 -1
- data/.dassie/app/forms/generic_work_resource_form.rb +20 -0
- data/.dassie/app/indexers/generic_work_resource_indexer.rb +16 -0
- data/.dassie/app/models/admin_set_resource.rb +9 -0
- data/.dassie/app/models/collection_resource.rb +2 -0
- data/.dassie/app/models/file_set.rb +2 -0
- data/.dassie/app/models/generic_work_resource.rb +10 -0
- data/.dassie/app/views/hyrax/generic_work_resources/_generic_work_resource.html.erb +2 -0
- data/.dassie/config/analytics.yml +6 -1
- data/.dassie/config/application.rb +24 -0
- data/.dassie/config/initializers/hyrax.rb +13 -3
- data/.dassie/config/initializers/wings.rb +109 -0
- data/.dassie/config/metadata/generic_work_resource.yaml +22 -0
- data/.dassie/config/valkyrie_index.yml +4 -10
- data/.dassie/db/migrate/20240506070809_valkyrie_id_to_string.rb +5 -0
- data/.dassie/db/schema.rb +2 -2
- data/.dassie/spec/indexers/generic_work_resource_indexer_spec.rb +13 -0
- data/.dassie/spec/models/generic_work_resource_spec.rb +12 -0
- data/.dassie/spec/views/generic_work_resources/_generic_work_resource.html.erb_spec.rb +7 -0
- data/.dockerignore +6 -4
- data/.github/release.yml +3 -0
- data/.github/workflows/lint-build-test.yml +130 -0
- data/.github/workflows/test-results.yml +40 -0
- data/.koppie/.env +7 -5
- data/.koppie/Gemfile +12 -1
- data/.koppie/config/analytics.yml +6 -1
- data/.koppie/config/environments/test.rb +2 -0
- data/.koppie/config/initializers/1_valkyrie.rb +6 -2
- data/.koppie/config/solr.yml +1 -1
- data/.regen +1 -1
- data/.rubocop.yml +5 -0
- data/Dockerfile +16 -36
- data/Gemfile +2 -0
- data/app/assets/javascripts/hydra-editor/field_manager.es6 +187 -0
- data/app/assets/javascripts/hyrax/analytics_events.js +48 -24
- data/app/assets/javascripts/hyrax/collapse.js +4 -4
- data/app/assets/javascripts/hyrax/file_manager/save_manager.es6 +2 -0
- data/app/assets/javascripts/hyrax/search.js +2 -3
- data/app/assets/javascripts/hyrax/select_work_type.es6 +3 -1
- data/app/assets/javascripts/hyrax/uploader.js +20 -18
- data/app/assets/javascripts/hyrax.js +1 -0
- data/app/assets/stylesheets/_bootstrap-default-overrides.scss +4 -0
- data/app/assets/stylesheets/hyrax/_card.scss +4 -0
- data/app/assets/stylesheets/hyrax/_catalog.scss +21 -0
- data/app/assets/stylesheets/hyrax/_collections.scss +1 -1
- data/app/assets/stylesheets/hyrax/_facets.scss +15 -3
- data/app/assets/stylesheets/hyrax/_featured.scss +4 -0
- data/app/assets/stylesheets/hyrax/_form.scss +4 -0
- data/app/assets/stylesheets/hyrax/_forms.scss +2 -1
- data/app/assets/stylesheets/hyrax/_nestable.scss +9 -8
- data/app/assets/stylesheets/hyrax/_select_work_type.scss +12 -0
- data/app/assets/stylesheets/hyrax/_styles.scss +4 -0
- data/app/assets/stylesheets/hyrax/_work-show.scss +3 -0
- data/app/controllers/concerns/hyrax/singular_subresource_controller.rb +7 -2
- data/app/controllers/concerns/hyrax/valkyrie_downloads_controller_behavior.rb +11 -2
- data/app/controllers/concerns/hyrax/works_controller_behavior.rb +9 -2
- data/app/controllers/hyrax/admin/analytics/collection_reports_controller.rb +2 -2
- data/app/controllers/hyrax/admin/analytics/work_reports_controller.rb +7 -8
- data/app/controllers/hyrax/dashboard/collections_controller.rb +2 -1
- data/app/controllers/hyrax/downloads_controller.rb +24 -3
- data/app/controllers/hyrax/file_sets_controller.rb +32 -6
- data/app/controllers/hyrax/my/works_controller.rb +20 -0
- data/app/controllers/hyrax/stats_controller.rb +1 -1
- data/app/controllers/hyrax/uploads_controller.rb +28 -2
- data/app/forms/hyrax/forms/admin/appearance.rb +1 -1
- data/app/forms/hyrax/forms/admin/collection_type_form.rb +1 -7
- data/app/forms/hyrax/forms/pcdm_collection_form.rb +9 -0
- data/app/forms/hyrax/forms/work_embargo_form.rb +6 -0
- data/app/forms/hyrax/forms/work_lease_form.rb +6 -0
- data/app/indexers/concerns/hyrax/location_indexer.rb +2 -2
- data/app/indexers/hyrax/indexers/file_set_indexer.rb +4 -0
- data/app/indexers/hyrax/indexers/resource_indexer.rb +1 -0
- data/app/indexers/hyrax/valkyrie_indexer.rb +3 -5
- data/app/jobs/migrate_files_to_valkyrie_job.rb +109 -0
- data/app/jobs/migrate_resources_job.rb +34 -0
- data/app/jobs/valkyrie_create_derivatives_job.rb +2 -1
- data/app/models/admin_set.rb +1 -0
- data/app/models/concerns/hyrax/ar_resource.rb +104 -0
- data/app/models/concerns/hyrax/solr_document/ordered_members.rb +2 -1
- data/app/models/concerns/hyrax/solr_document_behavior.rb +13 -2
- data/app/models/concerns/hyrax/valkyrie_lazy_migration.rb +82 -0
- data/app/models/file_download_stat.rb +1 -1
- data/app/models/file_view_stat.rb +1 -1
- data/app/models/hyrax/collection_type.rb +12 -4
- data/app/models/hyrax/file_metadata.rb +19 -0
- data/app/models/hyrax/file_set.rb +25 -0
- data/app/models/hyrax/model_registry.rb +2 -3
- data/app/models/hyrax/resource.rb +5 -0
- data/app/models/hyrax/statistic.rb +12 -37
- data/app/presenters/hyrax/file_set_presenter.rb +2 -1
- data/app/presenters/hyrax/file_usage.rb +3 -3
- data/app/presenters/hyrax/iiif_manifest_presenter.rb +2 -1
- data/app/presenters/hyrax/member_presenter_factory.rb +7 -1
- data/app/presenters/hyrax/menu_presenter.rb +1 -1
- data/app/presenters/hyrax/stats_usage_presenter.rb +2 -1
- data/app/presenters/hyrax/work_show_presenter.rb +13 -17
- data/app/presenters/hyrax/work_usage.rb +5 -2
- data/app/search_builders/hyrax/expired_embargo_search_builder.rb +7 -1
- data/app/search_builders/hyrax/expired_lease_search_builder.rb +7 -1
- data/app/search_builders/hyrax/filter_by_type.rb +1 -3
- data/app/search_builders/hyrax/valkyrie_abstract_type_relation.rb +7 -2
- data/app/services/hyrax/access_control_list.rb +1 -1
- data/app/services/hyrax/admin_set_create_service.rb +16 -5
- data/app/services/hyrax/admin_set_service.rb +2 -1
- data/app/services/hyrax/analytics/ga4/base.rb +96 -0
- data/app/services/hyrax/analytics/ga4/events.rb +25 -0
- data/app/services/hyrax/analytics/ga4/events_daily.rb +36 -0
- data/app/services/hyrax/analytics/ga4/visits.rb +33 -0
- data/app/services/hyrax/analytics/ga4/visits_daily.rb +24 -0
- data/app/services/hyrax/analytics/ga4.rb +204 -0
- data/app/services/hyrax/analytics/google.rb +16 -2
- data/app/services/hyrax/analytics/matomo.rb +16 -3
- data/app/services/hyrax/analytics/results.rb +6 -0
- data/app/services/hyrax/custom_queries/find_access_control.rb +1 -1
- data/app/services/hyrax/custom_queries/find_by_date_range.rb +6 -23
- data/app/services/hyrax/custom_queries/find_collections_by_type.rb +2 -2
- data/app/services/hyrax/custom_queries/find_count_by.rb +3 -31
- data/app/services/hyrax/custom_queries/find_file_metadata.rb +2 -2
- data/app/services/hyrax/custom_queries/find_models_by_access.rb +5 -27
- data/app/services/hyrax/embargo_manager.rb +2 -1
- data/app/services/hyrax/listeners/file_listener.rb +2 -2
- data/app/services/hyrax/lock_manager.rb +6 -6
- data/app/services/hyrax/lockable.rb +4 -3
- data/app/services/hyrax/simple_schema_loader.rb +1 -1
- data/app/services/hyrax/solr_service.rb +22 -8
- data/app/services/hyrax/statistics/query_service.rb +1 -1
- data/app/services/hyrax/statistics/works/over_time.rb +1 -1
- data/app/services/hyrax/thumbnail_path_service.rb +2 -0
- data/app/services/hyrax/user_stat_importer.rb +5 -5
- data/app/services/hyrax/valkyrie_upload.rb +9 -7
- data/app/services/hyrax/versioning_service.rb +10 -2
- data/app/services/hyrax/work_query_service.rb +2 -2
- data/app/services/migrate_resource_service.rb +55 -0
- data/app/views/_controls.html.erb +5 -5
- data/app/views/_masthead.html.erb +1 -1
- data/app/views/catalog/_search_form.html.erb +9 -16
- data/app/views/catalog/_thumbnail_list_collection.html.erb +1 -1
- data/app/views/catalog/_thumbnail_list_default.html.erb +2 -2
- data/app/views/hyrax/admin/analytics/collection_reports/index.html.erb +4 -4
- data/app/views/hyrax/admin/analytics/work_reports/index.html.erb +1 -1
- data/app/views/hyrax/admin/collection_types/_form.html.erb +4 -4
- data/app/views/hyrax/admin/collection_types/index.html.erb +1 -1
- data/app/views/hyrax/admin/features/index.html.erb +1 -1
- data/app/views/hyrax/base/_file_manager_actions.html.erb +1 -1
- data/app/views/hyrax/base/_file_manager_member.html.erb +7 -4
- data/app/views/hyrax/base/_file_manager_thumbnail.html.erb +1 -1
- data/app/views/hyrax/base/_form_files.html.erb +1 -1
- data/app/views/hyrax/base/_form_member_of_collections.html.erb +4 -0
- data/app/views/hyrax/base/_show_actions.html.erb +7 -8
- data/app/views/hyrax/base/_work_button_row.html.erb +1 -1
- data/app/views/hyrax/batch_select/_add_button.html.erb +1 -1
- data/app/views/hyrax/content_blocks/_form.html.erb +3 -3
- data/app/views/hyrax/dashboard/_sidebar.html.erb +1 -1
- data/app/views/hyrax/dashboard/_user_activity.html.erb +2 -2
- data/app/views/hyrax/dashboard/collections/_form.html.erb +4 -4
- data/app/views/hyrax/dashboard/collections/_form_share.html.erb +6 -4
- data/app/views/hyrax/dashboard/collections/_list_collections.html.erb +1 -1
- data/app/views/hyrax/dashboard/collections/_show_document_list_row.html.erb +1 -1
- data/app/views/hyrax/dashboard/show_admin.html.erb +18 -19
- data/app/views/hyrax/dashboard/sidebar/_activity.html.erb +1 -1
- data/app/views/hyrax/embargoes/_list_expired_active_embargoes.html.erb +7 -7
- data/app/views/hyrax/file_sets/_actions.html.erb +9 -1
- data/app/views/hyrax/file_sets/_permission_form.html.erb +4 -2
- data/app/views/hyrax/file_sets/_show_actions.html.erb +1 -1
- data/app/views/hyrax/homepage/_featured.html.erb +1 -1
- data/app/views/hyrax/homepage/_recent_document.html.erb +2 -2
- data/app/views/hyrax/leases/_list_expired_active_leases.html.erb +6 -6
- data/app/views/hyrax/my/collections/_list_collections.html.erb +1 -1
- data/app/views/hyrax/my/collections/_tabs.html.erb +1 -1
- data/app/views/hyrax/pages/_form.html.erb +8 -8
- data/app/views/hyrax/transfers/_received.html.erb +1 -1
- data/app/views/hyrax/uploads/create.json.jbuilder +2 -2
- data/app/views/hyrax/users/_activity_log.html.erb +15 -9
- data/app/views/hyrax/users/_user_row.html.erb +6 -3
- data/app/views/hyrax/users/_vitals.html.erb +3 -2
- data/app/views/layouts/_head_tag_content.html.erb +2 -0
- data/app/views/shared/_appearance_styles.html.erb +5 -1
- data/app/views/shared/_ga4.html.erb +11 -0
- data/app/views/shared/_select_work_type_modal.html.erb +10 -1
- data/bin/db-migrate-seed.sh +3 -3
- data/bin/dev-entrypoint.sh +7 -2
- data/bin/{db-wait.sh → service-wait.sh} +1 -1
- data/bin/worker-entrypoint.sh +8 -0
- data/chart/hyrax/templates/deployment-worker.yaml +2 -2
- data/config/locales/hyrax.en.yml +4 -2
- data/config/metadata/basic_metadata.yaml +20 -0
- data/config/metadata/hyrax_internal_metadata.yaml +1 -1
- data/docker-compose-dassie.yml +167 -0
- data/docker-compose-koppie.yml +21 -36
- data/docker-compose-sirenia.yml +50 -44
- data/docker-compose.yml +2 -183
- data/documentation/developing-your-hyrax-based-app.md +2 -2
- data/hyrax.gemspec +5 -4
- data/lib/freyja/custom_query_container.rb +5 -0
- data/lib/freyja/metadata_adapter.rb +32 -0
- data/lib/freyja/persister.rb +42 -0
- data/lib/freyja/query_service.rb +20 -0
- data/lib/freyja/resource_factory.rb +8 -0
- data/lib/freyja.rb +14 -0
- data/lib/frigg/custom_query_container.rb +5 -0
- data/lib/frigg/metadata_adapter.rb +22 -0
- data/lib/frigg/persister.rb +33 -0
- data/lib/frigg/query_service.rb +15 -0
- data/lib/frigg.rb +13 -0
- data/lib/generators/hyrax/install_generator.rb +5 -0
- data/lib/generators/hyrax/templates/config/analytics.yml +6 -1
- data/lib/generators/hyrax/templates/config/initializers/1_valkyrie.rb +6 -2
- data/lib/generators/hyrax/templates/config/valkyrie_index.yml +1 -1
- data/lib/goddess/custom_query_container.rb +71 -0
- data/lib/goddess/metadata.rb +13 -0
- data/lib/goddess/query.rb +176 -0
- data/lib/hyrax/configuration.rb +83 -0
- data/lib/hyrax/engine.rb +2 -0
- data/lib/hyrax/form_fields.rb +1 -3
- data/lib/hyrax/name.rb +5 -0
- data/lib/hyrax/rubocop/custom_cops.rb +30 -0
- data/lib/hyrax/specs/capybara.rb +10 -6
- data/lib/hyrax/specs/shared_specs/factories/admin_sets.rb +2 -0
- data/lib/hyrax/specs/shared_specs/factories/hyrax_embargo.rb +4 -0
- data/lib/hyrax/specs/shared_specs/factories/hyrax_lease.rb +4 -0
- data/lib/hyrax/specs/shared_specs/factories/hyrax_work.rb +16 -2
- data/lib/hyrax/specs/shared_specs/hydra_works.rb +1 -1
- data/lib/hyrax/transactions/admin_set_destroy.rb +2 -1
- data/lib/hyrax/transactions/collection_destroy.rb +2 -1
- data/lib/hyrax/transactions/container.rb +9 -0
- data/lib/hyrax/transactions/steps/add_file_sets.rb +2 -1
- data/lib/hyrax/transactions/steps/delete_permission_template.rb +30 -0
- data/lib/hyrax/transactions/steps/delete_resource.rb +1 -1
- data/lib/hyrax/transactions/steps/save_collection_logo.rb +2 -1
- data/lib/hyrax/valkyrie_can_can_adapter.rb +8 -1
- data/lib/hyrax/version.rb +1 -1
- data/lib/wings/active_fedora_converter.rb +13 -5
- data/lib/wings/converter_value_mapper.rb +1 -0
- data/lib/wings/services/custom_queries/find_collections_by_type.rb +2 -1
- data/lib/wings/services/custom_queries/find_file_metadata.rb +2 -2
- data/lib/wings/setup.rb +12 -3
- data/lib/wings/transformer_value_mapper.rb +5 -1
- data/lib/wings/valkyrie/persister.rb +3 -1
- data/template.rb +1 -1
- metadata +77 -19
- data/.koppie/scripts/db-migrate-seed.sh +0 -9
- data/.koppie/scripts/entrypoint.sh +0 -10
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hyrax
|
|
4
|
+
##
|
|
5
|
+
# This mixin is for {Valkyrie::Resource} objects to be able to read/write the same Solr document
|
|
6
|
+
# as their corresponding {ActiveFedora::Base} object.
|
|
7
|
+
#
|
|
8
|
+
# @see https://github.com/samvera/hyrax/pull/6221 Discussion about having one indexed document
|
|
9
|
+
module ValkyrieLazyMigration
|
|
10
|
+
##
|
|
11
|
+
# Copy over even more of the {Wings::ModelRegistery} legacy model's model_name properties
|
|
12
|
+
#
|
|
13
|
+
# @note When we remove {Wings}, the downstream implementers will need to explicitly craft the
|
|
14
|
+
# properties of the model_name.
|
|
15
|
+
class ResourceName < ResourceName
|
|
16
|
+
def initialize(klass, *args)
|
|
17
|
+
super
|
|
18
|
+
|
|
19
|
+
legacy_model = Wings::ModelRegistry.lookup(klass)
|
|
20
|
+
return unless legacy_model
|
|
21
|
+
|
|
22
|
+
instance_variables.each do |ivar|
|
|
23
|
+
next if ivar == :@name
|
|
24
|
+
next if ivar == :@klass
|
|
25
|
+
instance_variable_set(ivar, legacy_model.model_name.send(ivar[1..-1]))
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
attr_reader :klass
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
##
|
|
32
|
+
# This function helps configuration a work for a valkyrie migration; namely by helping re-use
|
|
33
|
+
# an existing SOLR document, by specifying that the given :klass is a migration :from another
|
|
34
|
+
# class.
|
|
35
|
+
#
|
|
36
|
+
# @note This is similar to the {Wings::ModelRegistry.register}, but is envisioned as part of
|
|
37
|
+
# the Frigg and Freyja adapters for Postges and Fedora lazy migrations.
|
|
38
|
+
#
|
|
39
|
+
# @param klass [Hyrax::Resource, .attribute]
|
|
40
|
+
# @param from [ActiveFedora::Base, .to_rdf_representation]
|
|
41
|
+
# @param name_class [Hyrax::Name] responsible, in part, for determining the various routing
|
|
42
|
+
# paths you might use.
|
|
43
|
+
#
|
|
44
|
+
# @example
|
|
45
|
+
# class MyWork < ActiveFedora::Base
|
|
46
|
+
# end
|
|
47
|
+
#
|
|
48
|
+
# class MyWorkResource < Hyrax::Resource
|
|
49
|
+
# Hyrax::ValkyrieLazyMigration.migrating(self, from: MyWork)
|
|
50
|
+
# end
|
|
51
|
+
def self.migrating(klass, from:, name_class: Hyrax::ValkyrieLazyMigration::ResourceName)
|
|
52
|
+
Wings::ModelRegistry.register(klass, from)
|
|
53
|
+
from.singleton_class.define_method(:migrating_from) { from }
|
|
54
|
+
from.singleton_class.define_method(:migrating_to) { klass }
|
|
55
|
+
klass.singleton_class.define_method(:migrating_from) { from }
|
|
56
|
+
klass.singleton_class.define_method(:migrating_to) { klass }
|
|
57
|
+
klass.singleton_class.define_method(:_hyrax_default_name_class) { name_class }
|
|
58
|
+
klass.singleton_class.define_method(:to_rdf_representation) { migrating_from.to_rdf_representation }
|
|
59
|
+
|
|
60
|
+
klass.include(self)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
extend ActiveSupport::Concern
|
|
64
|
+
|
|
65
|
+
included do
|
|
66
|
+
attribute :internal_resource, Valkyrie::Types::Any.default(to_rdf_representation.freeze), internal: true
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def members
|
|
70
|
+
return @members if @members.present?
|
|
71
|
+
@members = member_ids.map do |id|
|
|
72
|
+
Hyrax.query_service.find_by(id: id)
|
|
73
|
+
rescue Valkyrie::Persistence::ObjectNotFoundError
|
|
74
|
+
Rails.logger.warn("Could not find member #{id} for #{self.id}")
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def to_solr
|
|
79
|
+
Hyrax::ValkyrieIndexer.for(resource: self).to_solr
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -100,12 +100,20 @@ module Hyrax
|
|
|
100
100
|
|
|
101
101
|
##
|
|
102
102
|
# @return [Enumerable<Collection, PcdmCollection>]
|
|
103
|
-
def collections(use_valkyrie: Hyrax.config.use_valkyrie
|
|
103
|
+
def collections(use_valkyrie: Hyrax.config.use_valkyrie?, model: Hyrax.config.collection_class)
|
|
104
104
|
return [] unless id
|
|
105
|
-
return Hyrax.custom_queries.find_collections_by_type(global_id: to_global_id.to_s) if use_valkyrie
|
|
105
|
+
return Hyrax.custom_queries.find_collections_by_type(global_id: to_global_id.to_s, model:) if use_valkyrie
|
|
106
106
|
ActiveFedora::Base.where(Hyrax.config.collection_type_index_field.to_sym => to_global_id.to_s)
|
|
107
107
|
end
|
|
108
108
|
|
|
109
|
+
# Query solr to see if any collections of this type exist
|
|
110
|
+
# This should be much more performant for certain adapters than calling collections.any?
|
|
111
|
+
# @return [Boolean] True if there are any collections of this collection type in the repository
|
|
112
|
+
def collections?
|
|
113
|
+
return false unless id
|
|
114
|
+
Hyrax::SolrQueryService.new.with_field_pairs(field_pairs: { Hyrax.config.collection_type_index_field.to_sym => to_global_id.to_s }).with_model(model: Hyrax.config.collection_class.to_rdf_representation).count > 0
|
|
115
|
+
end
|
|
116
|
+
|
|
109
117
|
# @return [Boolean] True if this is the Admin Set type
|
|
110
118
|
def admin_set?
|
|
111
119
|
machine_id == ADMIN_SET_MACHINE_ID
|
|
@@ -154,7 +162,7 @@ module Hyrax
|
|
|
154
162
|
end
|
|
155
163
|
|
|
156
164
|
def ensure_no_collections
|
|
157
|
-
return true unless collections
|
|
165
|
+
return true unless collections?
|
|
158
166
|
errors[:base] << I18n.t('hyrax.admin.collection_types.errors.not_empty')
|
|
159
167
|
throw :abort
|
|
160
168
|
end
|
|
@@ -172,7 +180,7 @@ module Hyrax
|
|
|
172
180
|
end
|
|
173
181
|
|
|
174
182
|
def ensure_no_settings_changes_if_collections_exist
|
|
175
|
-
return true unless collections
|
|
183
|
+
return true unless collections?
|
|
176
184
|
return true unless collection_type_settings_changed?
|
|
177
185
|
errors[:base] << I18n.t('hyrax.admin.collection_types.errors.no_settings_change_if_not_empty')
|
|
178
186
|
throw :abort
|
|
@@ -42,6 +42,18 @@ module Hyrax
|
|
|
42
42
|
|
|
43
43
|
THUMBNAIL = ::Valkyrie::Vocab::PCDMUse.ThumbnailImage # for compatibility with earlier versions of Hyrax; prefer +THUMBNAIL_IMAGE+
|
|
44
44
|
|
|
45
|
+
# @return [Array<RDF::URI>] list of all uses
|
|
46
|
+
def use_list
|
|
47
|
+
[ORIGINAL_FILE,
|
|
48
|
+
THUMBNAIL_IMAGE,
|
|
49
|
+
EXTRACTED_TEXT,
|
|
50
|
+
INTERMEDIATE_FILE,
|
|
51
|
+
PRESERVATION_FILE,
|
|
52
|
+
SERVICE_FILE,
|
|
53
|
+
TRANSCRIPT]
|
|
54
|
+
end
|
|
55
|
+
module_function :use_list
|
|
56
|
+
|
|
45
57
|
##
|
|
46
58
|
# @param use [RDF::URI, Symbol]
|
|
47
59
|
#
|
|
@@ -166,6 +178,13 @@ module Hyrax
|
|
|
166
178
|
pcdm_use.include?(Use::EXTRACTED_TEXT)
|
|
167
179
|
end
|
|
168
180
|
|
|
181
|
+
##
|
|
182
|
+
# Filters out uses not recognized by Hyrax (e.g. http://fedora.info/definitions/v4/repository#Binary)
|
|
183
|
+
# @return [Array]
|
|
184
|
+
def filtered_pcdm_use
|
|
185
|
+
pcdm_use.select { |use| Use.use_list.include?(use) }
|
|
186
|
+
end
|
|
187
|
+
|
|
169
188
|
##
|
|
170
189
|
# @return [String]
|
|
171
190
|
def to_rdf_representation
|
|
@@ -106,6 +106,31 @@ module Hyrax
|
|
|
106
106
|
extracted_text&.id
|
|
107
107
|
end
|
|
108
108
|
|
|
109
|
+
##
|
|
110
|
+
# @return [Array] All ids, extensions, mime types, names, and uses
|
|
111
|
+
# @example
|
|
112
|
+
# [{:id=>"123", :extension=>"pdf", :mime_type=>"application/pdf", :name=>nil, :use=>"OriginalFile"},
|
|
113
|
+
# {:id=>"234", :extension=>"jpeg", :mime_type=>"application/octet-stream", :name=>"thumbnail", :use=>"ThumbnailImage"}]
|
|
114
|
+
# rubocop:disable Metrics/MethodLength
|
|
115
|
+
def extensions_and_mime_types
|
|
116
|
+
return [] if file_ids.empty?
|
|
117
|
+
Hyrax.custom_queries.find_files(file_set: self).each_with_object([]) do |fm, arr|
|
|
118
|
+
next unless fm.original_filename
|
|
119
|
+
extension = File.extname(fm.original_filename)
|
|
120
|
+
next if extension.empty?
|
|
121
|
+
use = fm.filtered_pcdm_use.first.to_s.split("#").last
|
|
122
|
+
name = use == 'OriginalFile' ? nil : File.basename(fm.original_filename, extension).split('-').last
|
|
123
|
+
arr << {
|
|
124
|
+
id: fm.id.to_s,
|
|
125
|
+
extension: extension[1..], # remove leading '.'
|
|
126
|
+
mime_type: fm.mime_type,
|
|
127
|
+
name: name,
|
|
128
|
+
use: use
|
|
129
|
+
}
|
|
130
|
+
end
|
|
131
|
+
# rubocop:enable Metrics/MethodLength
|
|
132
|
+
end
|
|
133
|
+
|
|
109
134
|
##
|
|
110
135
|
# @return [Valkyrie::ID]
|
|
111
136
|
def representative_id
|
|
@@ -85,7 +85,8 @@ module Hyrax
|
|
|
85
85
|
#
|
|
86
86
|
# @todo Consider the Wings::ModelRegistry and how we perform mappings.
|
|
87
87
|
def self.work_class_names
|
|
88
|
-
Hyrax.config.registered_curation_concern_types
|
|
88
|
+
@work_class_names ||= (Hyrax.config.registered_curation_concern_types +
|
|
89
|
+
Array(Rails.application.class.try(:work_types))).map(&:to_s).uniq
|
|
89
90
|
end
|
|
90
91
|
|
|
91
92
|
def self.work_classes
|
|
@@ -101,11 +102,9 @@ module Hyrax
|
|
|
101
102
|
def self.classes_from(strings)
|
|
102
103
|
strings.map(&:safe_constantize).compact.uniq
|
|
103
104
|
end
|
|
104
|
-
private_class_method :classes_from
|
|
105
105
|
|
|
106
106
|
def self.rdf_representations_from(klasses)
|
|
107
107
|
klasses.map { |klass| klass.respond_to?(:to_rdf_representation) ? klass.to_rdf_representation : klass.name }.uniq
|
|
108
108
|
end
|
|
109
|
-
private_class_method :rdf_representations_from
|
|
110
109
|
end
|
|
111
110
|
end
|
|
@@ -149,6 +149,11 @@ module Hyrax
|
|
|
149
149
|
self.class.work?
|
|
150
150
|
end
|
|
151
151
|
|
|
152
|
+
# Its nice to know if a record is still in AF or not
|
|
153
|
+
def wings?
|
|
154
|
+
respond_to?(:head) && respond_to?(:tail)
|
|
155
|
+
end
|
|
156
|
+
|
|
152
157
|
def ==(other)
|
|
153
158
|
attributes.except(:created_at, :updated_at) == other.attributes.except(:created_at, :updated_at) if other.respond_to?(:attributes)
|
|
154
159
|
end
|
|
@@ -25,47 +25,22 @@ module Hyrax
|
|
|
25
25
|
combined_stats object, start_date, cache_column, event_type, user_id
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
# Hyrax::Download is sent to Hyrax::Analytics.profile as #hyrax__download
|
|
29
|
-
# see Legato::ProfileMethods.method_name_from_klass
|
|
30
|
-
def ga_statistics(start_date, object)
|
|
31
|
-
path = polymorphic_path(object)
|
|
32
|
-
profile = Hyrax::Analytics.profile
|
|
33
|
-
unless profile
|
|
34
|
-
Hyrax.logger.error("Google Analytics profile has not been established. Unable to fetch statistics.")
|
|
35
|
-
return []
|
|
36
|
-
end
|
|
37
|
-
profile.hyrax__pageview(sort: 'date',
|
|
38
|
-
start_date: start_date,
|
|
39
|
-
end_date: Date.yesterday,
|
|
40
|
-
limit: 10_000)
|
|
41
|
-
.for_path(path)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
28
|
def query_works(query)
|
|
45
|
-
models = Hyrax.
|
|
46
|
-
Hyrax::SolrService.
|
|
29
|
+
models = Hyrax::ModelRegistry.work_rdf_representations.map { |m| "\"#{m}\"" }
|
|
30
|
+
response = Hyrax::SolrService.get(fq: "has_model_ssim:(#{models.join(' OR ')})", 'facet.field': query, 'facet.missing': true, rows: 0)
|
|
31
|
+
Hash[*response['facet_counts']['facet_fields'][query]]
|
|
47
32
|
end
|
|
48
33
|
|
|
49
34
|
def work_types
|
|
50
|
-
|
|
51
|
-
|
|
35
|
+
types = query_works("human_readable_type_sim")
|
|
36
|
+
types['Unknown'] = types.delete(nil)
|
|
37
|
+
types
|
|
52
38
|
end
|
|
53
39
|
|
|
54
40
|
def resource_types
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if y["resource_type_tesim"].nil? || (y["resource_type_tesim"] == [""])
|
|
59
|
-
resource_types.push("Unknown")
|
|
60
|
-
elsif y["resource_type_tesim"].count > 1
|
|
61
|
-
y["resource_type_tesim"].each do |t|
|
|
62
|
-
resource_types.push(t)
|
|
63
|
-
end
|
|
64
|
-
else
|
|
65
|
-
resource_types.push(y["resource_type_tesim"].join(""))
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
resource_types.group_by { |rt| rt }.transform_values(&:count)
|
|
41
|
+
types = query_works("resource_type_sim")
|
|
42
|
+
types['Unknown'] = types.delete(nil)
|
|
43
|
+
types
|
|
69
44
|
end
|
|
70
45
|
|
|
71
46
|
private
|
|
@@ -80,10 +55,10 @@ module Hyrax
|
|
|
80
55
|
stat_cache_info = cached_stats(object, start_date, object_method)
|
|
81
56
|
stats = stat_cache_info[:cached_stats]
|
|
82
57
|
if stat_cache_info[:ga_start_date] < Time.zone.today
|
|
83
|
-
|
|
84
|
-
|
|
58
|
+
page_stats = Hyrax::Analytics.page_statistics(stat_cache_info[:ga_start_date], object)
|
|
59
|
+
page_stats.each do |stat|
|
|
85
60
|
lstat = build_for(object, date: stat[:date], object_method => stat[ga_key], user_id: user_id)
|
|
86
|
-
lstat.save unless
|
|
61
|
+
lstat.save unless stat[:date].to_date == Time.zone.today
|
|
87
62
|
stats << lstat
|
|
88
63
|
end
|
|
89
64
|
end
|
|
@@ -20,7 +20,8 @@ module Hyrax
|
|
|
20
20
|
|
|
21
21
|
# CurationConcern methods
|
|
22
22
|
delegate :stringify_keys, :human_readable_type, :collection?, :image?, :video?,
|
|
23
|
-
:audio?, :pdf?, :office_document?, :representative_id, :to_s,
|
|
23
|
+
:audio?, :pdf?, :office_document?, :representative_id, :to_s,
|
|
24
|
+
:extensions_and_mime_types, to: :solr_document
|
|
24
25
|
|
|
25
26
|
# Methods used by blacklight helpers
|
|
26
27
|
delegate :has?, :first, :fetch, to: :solr_document
|
|
@@ -5,7 +5,7 @@ module Hyrax
|
|
|
5
5
|
# and prepares it for visualization in /app/views/stats/file.html.erb
|
|
6
6
|
class FileUsage < StatsUsagePresenter
|
|
7
7
|
def initialize(id)
|
|
8
|
-
self.model =
|
|
8
|
+
self.model = Hyrax.query_service.find_by(id: id)
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
alias file model
|
|
@@ -29,11 +29,11 @@ module Hyrax
|
|
|
29
29
|
private
|
|
30
30
|
|
|
31
31
|
def downloads
|
|
32
|
-
to_flots(FileDownloadStat.statistics(model, created, user_id))
|
|
32
|
+
@downloads ||= to_flots(FileDownloadStat.statistics(model, created, user_id))
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
def pageviews
|
|
36
|
-
to_flots(FileViewStat.statistics(model, created, user_id))
|
|
36
|
+
@pageviews ||= to_flots(FileViewStat.statistics(model, created, user_id))
|
|
37
37
|
end
|
|
38
38
|
end
|
|
39
39
|
end
|
|
@@ -56,7 +56,8 @@ module Hyrax
|
|
|
56
56
|
##
|
|
57
57
|
# @return [Boolean]
|
|
58
58
|
def file_set?
|
|
59
|
-
model.try(:file_set?)
|
|
59
|
+
return true if model.try(:file_set?)
|
|
60
|
+
(Array(model[:has_model_ssim]) & Hyrax::ModelRegistry.file_set_rdf_representations).any?
|
|
60
61
|
end
|
|
61
62
|
|
|
62
63
|
##
|
|
@@ -47,7 +47,9 @@ module Hyrax
|
|
|
47
47
|
# in order.
|
|
48
48
|
# Arbitrarily maxed at 10 thousand; had to specify rows due to solr's default of 10
|
|
49
49
|
def file_set_ids
|
|
50
|
-
|
|
50
|
+
pairs = file_set_models.map { |model| ["has_model_ssim", model] }
|
|
51
|
+
query = Hyrax::SolrQueryBuilderService.construct_query(pairs, "OR")
|
|
52
|
+
@file_set_ids ||= Hyrax::SolrService.query(query,
|
|
51
53
|
rows: 10_000,
|
|
52
54
|
fl: Hyrax.config.id_field,
|
|
53
55
|
fq: "{!join from=ordered_targets_ssim to=id}id:\"#{id}/list_source\"")
|
|
@@ -61,5 +63,9 @@ module Hyrax
|
|
|
61
63
|
def composite_presenter_class
|
|
62
64
|
CompositePresenterFactory.new(file_presenter_class, work_presenter_class, ordered_ids & file_set_ids)
|
|
63
65
|
end
|
|
66
|
+
|
|
67
|
+
def file_set_models
|
|
68
|
+
Hyrax::ModelRegistry.file_set_rdf_representations
|
|
69
|
+
end
|
|
64
70
|
end
|
|
65
71
|
end
|
|
@@ -33,7 +33,7 @@ module Hyrax
|
|
|
33
33
|
def nav_link(options, also_active_for: nil, **link_html_options)
|
|
34
34
|
active_urls = [options, also_active_for].compact
|
|
35
35
|
list_options = active_urls.any? { |url| current_page?(url) } ? { class: 'active nav-item' } : { class: 'nav-item' }
|
|
36
|
-
tag.li(list_options) do
|
|
36
|
+
tag.li(**list_options) do
|
|
37
37
|
link_to(options, link_html_options) do
|
|
38
38
|
yield
|
|
39
39
|
end
|
|
@@ -30,12 +30,13 @@ module Hyrax
|
|
|
30
30
|
def date_for_analytics
|
|
31
31
|
earliest = Hyrax.config.analytic_start_date
|
|
32
32
|
date_uploaded = string_to_date(model.date_uploaded)
|
|
33
|
-
date_analytics = date_uploaded ? date_uploaded : model.create_date
|
|
33
|
+
date_analytics = date_uploaded ? date_uploaded : (model.create_date || model.created_at)
|
|
34
34
|
return date_analytics if earliest.blank?
|
|
35
35
|
earliest > date_analytics ? earliest : date_analytics
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def string_to_date(date_str)
|
|
39
|
+
return date_str if date_str.is_a?(Date)
|
|
39
40
|
Time.zone.parse(date_str)
|
|
40
41
|
rescue ArgumentError, TypeError
|
|
41
42
|
nil
|
|
@@ -16,13 +16,6 @@ module Hyrax
|
|
|
16
16
|
self.collection_presenter_class = CollectionPresenter
|
|
17
17
|
self.presenter_factory_class = MemberPresenterFactory
|
|
18
18
|
|
|
19
|
-
# Methods used by blacklight helpers
|
|
20
|
-
delegate :has?, :first, :fetch, :export_formats, :export_as, to: :solr_document
|
|
21
|
-
|
|
22
|
-
# delegate fields from Hyrax::Works::Metadata to solr_document
|
|
23
|
-
delegate :based_near_label, :related_url, :depositor, :identifier, :resource_type,
|
|
24
|
-
:keyword, :itemtype, :admin_set, :rights_notes, :access_right, :abstract, to: :solr_document
|
|
25
|
-
|
|
26
19
|
# @param [SolrDocument] solr_document
|
|
27
20
|
# @param [Ability] current_ability
|
|
28
21
|
# @param [ActionDispatch::Request] request the http request context. Used so
|
|
@@ -33,20 +26,14 @@ module Hyrax
|
|
|
33
26
|
@request = request
|
|
34
27
|
end
|
|
35
28
|
|
|
29
|
+
# We cannot rely on the method missing to catch this delegation. Because
|
|
30
|
+
# most all objects implicitly implicitly implement #to_s
|
|
31
|
+
delegate :to_s, to: :solr_document
|
|
32
|
+
|
|
36
33
|
def page_title
|
|
37
34
|
"#{human_readable_type} | #{title.first} | ID: #{id} | #{I18n.t('hyrax.product_name')}"
|
|
38
35
|
end
|
|
39
36
|
|
|
40
|
-
# CurationConcern methods
|
|
41
|
-
delegate :stringify_keys, :human_readable_type, :collection?, :to_s, :suppressed?,
|
|
42
|
-
to: :solr_document
|
|
43
|
-
|
|
44
|
-
# Metadata Methods
|
|
45
|
-
delegate :title, :date_created, :description,
|
|
46
|
-
:creator, :contributor, :subject, :publisher, :language, :embargo_release_date,
|
|
47
|
-
:lease_expiration_date, :license, :source, :rights_statement, :thumbnail_id, :representative_id,
|
|
48
|
-
:rendering_ids, :member_of_collection_ids, :alternative_title, :bibliographic_citation, to: :solr_document
|
|
49
|
-
|
|
50
37
|
def workflow
|
|
51
38
|
@workflow ||= WorkflowPresenter.new(solr_document, current_ability)
|
|
52
39
|
end
|
|
@@ -259,6 +246,15 @@ module Hyrax
|
|
|
259
246
|
|
|
260
247
|
private
|
|
261
248
|
|
|
249
|
+
def method_missing(method_name, *args, &block)
|
|
250
|
+
return solr_document.public_send(method_name, *args, &block) if solr_document.respond_to?(method_name)
|
|
251
|
+
super
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def respond_to_missing?(method_name, include_private = false)
|
|
255
|
+
solr_document.respond_to?(method_name, include_private) || super
|
|
256
|
+
end
|
|
257
|
+
|
|
262
258
|
# list of item ids to display is based on ordered_ids
|
|
263
259
|
def authorized_item_ids(filter_unreadable: Flipflop.hide_private_items?)
|
|
264
260
|
@member_item_list_ids ||=
|
|
@@ -8,11 +8,14 @@ module Hyrax
|
|
|
8
8
|
# and prepares it for visualization in /app/views/stats/work.html.erb
|
|
9
9
|
class WorkUsage < StatsUsagePresenter
|
|
10
10
|
def initialize(id)
|
|
11
|
-
self.model = Hyrax
|
|
11
|
+
self.model = Hyrax.query_service.find_by(id: id)
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
alias work model
|
|
15
|
-
|
|
15
|
+
|
|
16
|
+
def to_s
|
|
17
|
+
model.title.first
|
|
18
|
+
end
|
|
16
19
|
|
|
17
20
|
def total_pageviews
|
|
18
21
|
pageviews.reduce(0) { |total, result| total + result[1].to_i }
|
|
@@ -6,7 +6,13 @@ module Hyrax
|
|
|
6
6
|
|
|
7
7
|
def only_expired_embargoes(solr_params)
|
|
8
8
|
solr_params[:fq] ||= []
|
|
9
|
-
solr_params[:fq] =
|
|
9
|
+
solr_params[:fq] = "embargo_release_date_dtsi:[* TO #{now}]"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def now
|
|
15
|
+
Hyrax::TimeService.time_in_utc.utc.xmlschema
|
|
10
16
|
end
|
|
11
17
|
end
|
|
12
18
|
end
|
|
@@ -6,7 +6,13 @@ module Hyrax
|
|
|
6
6
|
|
|
7
7
|
def only_expired_leases(solr_params)
|
|
8
8
|
solr_params[:fq] ||= []
|
|
9
|
-
solr_params[:fq] =
|
|
9
|
+
solr_params[:fq] = "lease_expiration_date_dtsi:[* TO #{now}]"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def now
|
|
15
|
+
Hyrax::TimeService.time_in_utc.utc.xmlschema
|
|
10
16
|
end
|
|
11
17
|
end
|
|
12
18
|
end
|
|
@@ -30,9 +30,7 @@ module Hyrax
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def models_to_solr_clause
|
|
33
|
-
models.
|
|
34
|
-
model.respond_to?(:to_rdf_representation) ? model.to_rdf_representation : model.name
|
|
35
|
-
end.join(',')
|
|
33
|
+
Hyrax::ModelRegistry.rdf_representations_from(models).join(',')
|
|
36
34
|
end
|
|
37
35
|
|
|
38
36
|
def generic_type_field
|
|
@@ -19,8 +19,13 @@ module Hyrax
|
|
|
19
19
|
Hyrax.query_service.custom_queries.find_count_by(models: allowable_types)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
def where(
|
|
23
|
-
|
|
22
|
+
def where(hash_or_string)
|
|
23
|
+
case hash_or_string
|
|
24
|
+
when String
|
|
25
|
+
Hyrax::SolrService.query(hash_or_string)
|
|
26
|
+
else
|
|
27
|
+
Hyrax.query_service.find_references_by(resource: hash_or_string.values.first, property: hash_or_string.keys.first)
|
|
28
|
+
end
|
|
24
29
|
end
|
|
25
30
|
|
|
26
31
|
def ==(other)
|
|
@@ -10,7 +10,7 @@ module Hyrax
|
|
|
10
10
|
# @see Hyrax::PermissionTemplate
|
|
11
11
|
# @see Sipity::Workflow
|
|
12
12
|
class AdminSetCreateService # rubocop:disable Metrics/ClassLength
|
|
13
|
-
DEFAULT_ID = '
|
|
13
|
+
DEFAULT_ID = 'admin_set_default'
|
|
14
14
|
DEFAULT_TITLE = ['Default Admin Set'].freeze
|
|
15
15
|
|
|
16
16
|
class_attribute :permissions_create_service, :default_admin_set_persister
|
|
@@ -124,11 +124,22 @@ module Hyrax
|
|
|
124
124
|
# do not support hardcoded IDs (e.g. postgres)
|
|
125
125
|
# @return [Hyrax::AdministrativeSet] the default admin set; nil if not found
|
|
126
126
|
def find_unsaved_default_admin_set
|
|
127
|
-
admin_set =
|
|
128
|
-
|
|
127
|
+
admin_set = begin
|
|
128
|
+
# to support repositories still using the deprecated 'admin_set/default' as DEFAULT_ID
|
|
129
|
+
Hyrax.query_service.find_by(id: 'admin_set/default')
|
|
130
|
+
rescue Ldp::BadRequest, Valkyrie::Persistence::ObjectNotFoundError
|
|
131
|
+
# Fedora 6.5+ does not support slashes in IDs, hence the need to rescue Ldp::BadRequest
|
|
132
|
+
# if an admin set with deprecated ID 'admin_set/default' does not exist, check again for admin set with DEFAULT_ID
|
|
133
|
+
begin
|
|
134
|
+
Hyrax.query_service.find_by(id: DEFAULT_ID)
|
|
135
|
+
rescue Valkyrie::Persistence::ObjectNotFoundError
|
|
136
|
+
nil
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
default_admin_set_persister.update(default_admin_set_id: admin_set.id.to_s) if admin_set.present? && save_default?
|
|
141
|
+
|
|
129
142
|
admin_set
|
|
130
|
-
rescue Valkyrie::Persistence::ObjectNotFoundError
|
|
131
|
-
# a default admin set hasn't been created yet
|
|
132
143
|
end
|
|
133
144
|
|
|
134
145
|
# @return [String | nil] the default admin set id; returns nil if not set
|
|
@@ -51,9 +51,10 @@ module Hyrax
|
|
|
51
51
|
# @return [Hash] admin set id keys and file count values
|
|
52
52
|
def count_files(admin_sets)
|
|
53
53
|
file_counts = Hash.new(0)
|
|
54
|
+
file_set_models = Hyrax::ModelRegistry.file_set_rdf_representations
|
|
54
55
|
admin_sets.each do |admin_set|
|
|
55
56
|
query = "{!join from=member_ids_ssim to=id}isPartOf_ssim:#{admin_set.id}"
|
|
56
|
-
file_results = Hyrax::SolrService.get(fq: [query, "{!terms f=has_model_ssim}
|
|
57
|
+
file_results = Hyrax::SolrService.get(fq: [query, "{!terms f=has_model_ssim}#{file_set_models.join(',')}"], rows: 0)
|
|
57
58
|
file_counts[admin_set.id] = file_results['response']['numFound']
|
|
58
59
|
end
|
|
59
60
|
file_counts
|