hyrax 3.2.0 → 3.3.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 (167) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +3 -6
  3. data/.dassie/.env +1 -2
  4. data/.dassie/Gemfile +7 -3
  5. data/.dassie/app/models/user.rb +0 -2
  6. data/.dassie/config/analytics.yml +12 -5
  7. data/.dassie/config/environments/development.rb +2 -0
  8. data/.dassie/config/initializers/hyrax.rb +2 -0
  9. data/.dassie/db/migrate/20210921150120_enable_uuid_extension.valkyrie_engine.rb +7 -0
  10. data/.dassie/db/migrate/20210921150121_create_orm_resources.valkyrie_engine.rb +19 -0
  11. data/.dassie/db/migrate/20210921150122_add_model_type_to_orm_resources.valkyrie_engine.rb +7 -0
  12. data/.dassie/db/migrate/20210921150123_change_model_type_to_internal_model.valkyrie_engine.rb +7 -0
  13. data/.dassie/db/migrate/20210921150124_create_path_gin_index.valkyrie_engine.rb +7 -0
  14. data/.dassie/db/migrate/20210921150125_create_internal_resource_index.valkyrie_engine.rb +7 -0
  15. data/.dassie/db/migrate/20210921150126_create_updated_at_index.valkyrie_engine.rb +7 -0
  16. data/.dassie/db/migrate/20210921150127_add_optimistic_locking_to_orm_resources.valkyrie_engine.rb +7 -0
  17. data/.dassie/db/migrate/20211130181150_create_default_administrative_set.rb +8 -0
  18. data/.dassie/db/schema.rb +20 -1
  19. data/.env +7 -4
  20. data/.github/workflows/main.yml +17 -0
  21. data/.github/workflows/release.yml +17 -0
  22. data/.gitignore +1 -0
  23. data/.regen +1 -1
  24. data/CONTAINERS.md +13 -10
  25. data/README.md +37 -0
  26. data/app/assets/javascripts/hyrax/admin/graphs.es6 +34 -37
  27. data/app/assets/javascripts/hyrax/analytics_events.js +69 -0
  28. data/app/assets/javascripts/hyrax/collapse.js +24 -0
  29. data/app/assets/javascripts/hyrax/collections.js +1 -2
  30. data/app/assets/javascripts/hyrax/ga_events.js +2 -8
  31. data/app/assets/javascripts/hyrax/reports-buttons.js +33 -0
  32. data/app/assets/javascripts/hyrax.js +2 -1
  33. data/app/assets/stylesheets/_bootstrap-default-overrides.scss +9 -0
  34. data/app/authorities/qa/authorities/collections.rb +4 -5
  35. data/app/authorities/qa/authorities/find_works.rb +1 -1
  36. data/app/controllers/concerns/hyrax/breadcrumbs_for_collection_analytics.rb +26 -0
  37. data/app/controllers/concerns/hyrax/breadcrumbs_for_works_analytics.rb +26 -0
  38. data/app/controllers/concerns/hyrax/controller.rb +22 -0
  39. data/app/controllers/hyrax/admin/analytics/analytics_controller.rb +40 -0
  40. data/app/controllers/hyrax/admin/analytics/collection_reports_controller.rb +61 -0
  41. data/app/controllers/hyrax/admin/analytics/work_reports_controller.rb +122 -0
  42. data/app/controllers/hyrax/collections_controller.rb +4 -1
  43. data/app/controllers/hyrax/dashboard/collections_controller.rb +15 -6
  44. data/app/controllers/hyrax/dashboard_controller.rb +8 -0
  45. data/app/controllers/hyrax/stats_controller.rb +3 -1
  46. data/app/forms/hyrax/forms/pcdm_collection_form.rb +3 -0
  47. data/app/indexers/hyrax/valkyrie_file_set_indexer.rb +1 -1
  48. data/app/jobs/characterize_job.rb +28 -1
  49. data/app/jobs/valkyrie_ingest_job.rb +56 -0
  50. data/app/models/concerns/hyrax/ability.rb +26 -5
  51. data/app/models/concerns/hyrax/solr_document/metadata.rb +1 -0
  52. data/app/models/file_download_stat.rb +4 -4
  53. data/app/models/hyrax/default_administrative_set.rb +42 -0
  54. data/app/models/hyrax/statistic.rb +31 -4
  55. data/app/presenters/hyrax/admin/dashboard_presenter.rb +8 -6
  56. data/app/presenters/hyrax/admin/repository_growth_presenter.rb +10 -5
  57. data/app/presenters/hyrax/admin/user_activity_presenter.rb +8 -12
  58. data/app/presenters/hyrax/file_set_presenter.rb +2 -0
  59. data/app/presenters/hyrax/menu_presenter.rb +4 -0
  60. data/app/presenters/hyrax/pcdm_member_presenter_factory.rb +1 -1
  61. data/app/presenters/hyrax/work_show_presenter.rb +5 -2
  62. data/app/presenters/hyrax/work_usage.rb +1 -0
  63. data/app/search_builders/hyrax/README.md +1 -1
  64. data/app/search_builders/hyrax/dashboard/collections_search_builder.rb +1 -1
  65. data/app/search_builders/hyrax/my/collections_search_builder.rb +1 -1
  66. data/app/services/hyrax/admin_set_create_service.rb +76 -14
  67. data/app/services/hyrax/analytics/google/events.rb +37 -0
  68. data/app/services/hyrax/analytics/google/events_daily.rb +72 -0
  69. data/app/services/hyrax/analytics/google/visits.rb +44 -0
  70. data/app/services/hyrax/analytics/google/visits_daily.rb +49 -0
  71. data/app/services/hyrax/analytics/google.rb +204 -0
  72. data/app/services/hyrax/analytics/matomo.rb +193 -0
  73. data/app/services/hyrax/analytics/results.rb +79 -0
  74. data/app/services/hyrax/analytics.rb +12 -82
  75. data/app/services/hyrax/characterization/valkyrie_characterization_service.rb +134 -0
  76. data/app/services/hyrax/collections/nested_collection_query_service.rb +8 -3
  77. data/app/services/hyrax/listeners/acl_index_listener.rb +3 -1
  78. data/app/services/hyrax/listeners/active_fedora_acl_index_listener.rb +3 -1
  79. data/app/services/hyrax/listeners/batch_notification_listener.rb +3 -1
  80. data/app/services/hyrax/listeners/file_metadata_listener.rb +19 -0
  81. data/app/services/hyrax/listeners/file_set_lifecycle_listener.rb +6 -2
  82. data/app/services/hyrax/listeners/file_set_lifecycle_notification_listener.rb +6 -2
  83. data/app/services/hyrax/listeners/member_cleanup_listener.rb +3 -0
  84. data/app/services/hyrax/listeners/metadata_index_listener.rb +9 -3
  85. data/app/services/hyrax/listeners/object_lifecycle_listener.rb +9 -3
  86. data/app/services/hyrax/listeners/proxy_deposit_listener.rb +3 -1
  87. data/app/services/hyrax/listeners/trophy_cleanup_listener.rb +3 -0
  88. data/app/services/hyrax/listeners/workflow_listener.rb +3 -1
  89. data/app/services/hyrax/listeners.rb +8 -0
  90. data/app/services/hyrax/restriction_service.rb +4 -0
  91. data/app/services/hyrax/statistics/users/over_time.rb +8 -5
  92. data/app/services/hyrax/statistics/works/over_time.rb +10 -0
  93. data/app/services/hyrax/work_uploads_handler.rb +4 -1
  94. data/app/views/hyrax/admin/analytics/_date_range_form.html.erb +11 -0
  95. data/app/views/hyrax/admin/analytics/collection_reports/_custom_range.html.erb +39 -0
  96. data/app/views/hyrax/admin/analytics/collection_reports/_monthly_summary.html.erb +48 -0
  97. data/app/views/hyrax/admin/analytics/collection_reports/_summary.html.erb +55 -0
  98. data/app/views/hyrax/admin/analytics/collection_reports/_top_collections.html.erb +55 -0
  99. data/app/views/hyrax/admin/analytics/collection_reports/index.html.erb +70 -0
  100. data/app/views/hyrax/admin/analytics/collection_reports/show.html.erb +94 -0
  101. data/app/views/hyrax/admin/analytics/work_reports/_custom_range.html.erb +43 -0
  102. data/app/views/hyrax/admin/analytics/work_reports/_monthly_summary.html.erb +35 -0
  103. data/app/views/hyrax/admin/analytics/work_reports/_summary.html.erb +60 -0
  104. data/app/views/hyrax/admin/analytics/work_reports/_top_file_set_downloads.html.erb +33 -0
  105. data/app/views/hyrax/admin/analytics/work_reports/_top_works.html.erb +40 -0
  106. data/app/views/hyrax/admin/analytics/work_reports/_work_counts.html.erb +18 -0
  107. data/app/views/hyrax/admin/analytics/work_reports/_work_files.html.erb +41 -0
  108. data/app/views/hyrax/admin/analytics/work_reports/index.html.erb +77 -0
  109. data/app/views/hyrax/admin/analytics/work_reports/show.html.erb +90 -0
  110. data/app/views/hyrax/admin/stats/show.html.erb +1 -1
  111. data/app/views/hyrax/base/_relationships_parent_row.html.erb +0 -1
  112. data/app/views/hyrax/base/show.html.erb +6 -0
  113. data/app/views/hyrax/collections/show.html.erb +4 -0
  114. data/app/views/hyrax/dashboard/_repository_growth.html.erb +5 -5
  115. data/app/views/hyrax/dashboard/_resource_type_graph.html.erb +41 -0
  116. data/app/views/hyrax/dashboard/_sidebar.html.erb +4 -1
  117. data/app/views/hyrax/dashboard/_tabs.html.erb +11 -0
  118. data/app/views/hyrax/dashboard/_user_activity.html.erb +17 -23
  119. data/app/views/hyrax/dashboard/_user_activity_graph.html.erb +55 -0
  120. data/app/views/hyrax/dashboard/_visibility_graph.html.erb +31 -0
  121. data/app/views/hyrax/dashboard/_work_type_graph.html.erb +41 -0
  122. data/app/views/hyrax/dashboard/collections/_form.html.erb +2 -1
  123. data/app/views/hyrax/dashboard/show_admin.html.erb +24 -45
  124. data/app/views/hyrax/dashboard/sidebar/_activity.html.erb +22 -0
  125. data/app/views/hyrax/file_sets/_actions.html.erb +4 -3
  126. data/app/views/hyrax/file_sets/show.html.erb +6 -0
  127. data/app/views/hyrax/my/collections/index.html.erb +1 -1
  128. data/app/views/hyrax/stats/_downloads.html.erb +18 -0
  129. data/app/views/hyrax/stats/_pageviews.html.erb +18 -0
  130. data/app/views/hyrax/stats/work.html.erb +17 -9
  131. data/app/views/layouts/_head_tag_content.html.erb +7 -2
  132. data/app/views/{_ga.html.erb → shared/_ga.html.erb} +3 -7
  133. data/app/views/shared/_matomo.html.erb +15 -0
  134. data/chart/hyrax/Chart.yaml +1 -1
  135. data/chart/hyrax/values.yaml +1 -1
  136. data/config/i18n-tasks.yml +2 -2
  137. data/config/initializers/listeners.rb +5 -5
  138. data/config/locales/hyrax.de.yml +194 -0
  139. data/config/locales/hyrax.en.yml +190 -12
  140. data/config/locales/hyrax.es.yml +194 -0
  141. data/config/locales/hyrax.fr.yml +194 -0
  142. data/config/locales/hyrax.it.yml +194 -0
  143. data/config/locales/hyrax.pt-BR.yml +194 -0
  144. data/config/locales/hyrax.zh.yml +194 -0
  145. data/config/routes.rb +4 -0
  146. data/docker-compose.yml +3 -1
  147. data/documentation/developing-your-hyrax-based-app.md +2 -2
  148. data/documentation/legacyREADME.md +1 -1
  149. data/hyrax.gemspec +3 -1
  150. data/lib/generators/hyrax/templates/config/analytics.yml +13 -7
  151. data/lib/generators/hyrax/templates/config/initializers/hyrax.rb +0 -13
  152. data/lib/generators/hyrax/templates/db/migrate/20211130181150_create_default_administrative_set.rb.erb +8 -0
  153. data/lib/generators/hyrax/work/templates/feature_spec.rb.erb +3 -1
  154. data/lib/hyrax/configuration.rb +67 -5
  155. data/lib/hyrax/engine.rb +7 -6
  156. data/lib/hyrax/publisher.rb +4 -0
  157. data/lib/hyrax/transactions/admin_set_create.rb +22 -0
  158. data/lib/hyrax/transactions/container.rb +11 -0
  159. data/lib/hyrax/version.rb +1 -1
  160. data/lib/tasks/regenerate_derivatives.rake +1 -1
  161. data/lib/wings/setup.rb +15 -0
  162. data/lib/wings/valkyrie/persister.rb +16 -0
  163. data/template.rb +1 -1
  164. data/vendor/assets/javascripts/morris/morris.min.js +1 -7
  165. data/vendor/assets/stylesheets/morris.js/0.5.1/morris.css +1 -1
  166. metadata +87 -11
  167. data/app/views/hyrax/dashboard/_repository_objects.html.erb +0 -28
@@ -62,6 +62,21 @@ function getStatusSettings(){
62
62
  }
63
63
  }
64
64
 
65
+ function getStatusAnalytics(){
66
+ const resultDiv = document.getElementById('collapseAnalytics');
67
+ const isCollapsed = getCollapsedSettings();
68
+ if (resultDiv === null) {
69
+ return;
70
+ }
71
+ else if(isCollapsed){
72
+ resultDiv.classList.remove("in");
73
+ resultDiv.setAttribute("aria-expanded", "false");
74
+ }else{
75
+ resultDiv.classList.add("in");
76
+ resultDiv.setAttribute("aria-expanded", "true");
77
+ }
78
+ }
79
+
65
80
  function toggleCollapse(input){
66
81
  var type = input.href;
67
82
  var start = type.indexOf("#");
@@ -76,6 +91,15 @@ function toggleCollapse(input){
76
91
  }
77
92
  }
78
93
 
94
+ if (type === "collapseReports"){
95
+ const isCollapsedReports = getCollapsedReports();
96
+ if(isCollapsedReports){
97
+ localStorage.setItem('collapsedReports', 'un-collapsed');
98
+ }else{
99
+ localStorage.setItem('collapsedReports', 'collapsed');
100
+ }
101
+ }
102
+
79
103
  if (type === "collapseSettings"){
80
104
  const isCollapsedSettings = getCollapsedSettings();
81
105
  if(isCollapsedSettings){
@@ -319,10 +319,9 @@ Blacklight.onLoad(function () {
319
319
  submitModalAjax(url, 'POST', data, $(this));
320
320
  });
321
321
 
322
-
323
322
  // Handle add a subcollection button click on the collections show page
324
323
  $('.sub-collections-wrapper button.add-subcollection').on('click', function (e) {
325
324
  $('#add-subcollection-modal-' + $(this).data('presenterId')).modal('show');
326
325
  });
327
326
 
328
- });
327
+ });
@@ -1,10 +1,4 @@
1
1
  // Callbacks for tracking events using Google Analytics
2
-
3
- // Note: there is absence of testing here. I'm not sure how or to what extent we can test what's getting
4
- // sent to Google Analytics.
5
-
6
2
  $(document).on('click', '#file_download', function(e) {
7
- _gaq.push(['_trackEvent', 'Files', 'Downloaded', $(this).data('label')]);
8
-
9
- });
10
-
3
+ _gaq.push(['_trackEvent', 'Files', 'Downloaded', $(this).data('label')]);
4
+ });
@@ -0,0 +1,33 @@
1
+ Blacklight.onLoad(function() {
2
+ var summaryButton = $('#summary-btn');
3
+ var monthlyButton = $('#monthly-btn');
4
+ var rangeButton = $('#range-btn');
5
+
6
+ $('#monthly-btn').on('click', function() {
7
+ $(this).addClass('btn-primary');
8
+ $(this).removeClass('btn-default');
9
+ summaryButton.removeClass('btn-primary');
10
+ summaryButton.addClass('btn-default');
11
+ rangeButton.removeClass('btn-primary');
12
+ rangeButton.addClass('btn-default');
13
+ });
14
+
15
+ $('#summary-btn').on('click', function() {
16
+
17
+ $(this).addClass('btn-primary');
18
+ $(this).removeClass('btn-default');
19
+ monthlyButton.removeClass('btn-primary');
20
+ monthlyButton.addClass('btn-default');
21
+ rangeButton.removeClass('btn-primary');
22
+ rangeButton.addClass('btn-default');
23
+ });
24
+
25
+ $('#range-btn').on('click', function() {
26
+ $(this).addClass('btn-primary');
27
+ $(this).removeClass('btn-default');
28
+ monthlyButton.removeClass('btn-primary');
29
+ monthlyButton.addClass('btn-default');
30
+ summaryButton.removeClass('btn-primary');
31
+ summaryButton.addClass('btn-default');
32
+ });
33
+ });
@@ -56,7 +56,7 @@
56
56
  //= require hyrax/search
57
57
  //= require hyrax/content_blocks
58
58
  //= require hyrax/nav_safety
59
- //= require hyrax/ga_events
59
+ //= require hyrax/analytics_events
60
60
  //= require hyrax/select_submit
61
61
  //= require hyrax/tabs
62
62
  //= require hyrax/user_search
@@ -107,6 +107,7 @@
107
107
  //= require hyrax/i18n_helper
108
108
  //= require hyrax/collapse
109
109
  //= require hyrax/skip_to_content
110
+ //= require hyrax/reports-buttons
110
111
 
111
112
  // this needs to be after batch_select so that the form ids get setup correctly
112
113
  //= require hyrax/batch_edit
@@ -18,3 +18,12 @@ $brand-warning: #565653 !default;
18
18
  // * Darkens the text for disabled pagination buttons on every page.
19
19
  $breadcrumb-active-color: #4c4c4c;
20
20
  $pagination-disabled-color: #4c4c4c;
21
+
22
+ // Date picker on safari placeholder text was misaligned
23
+ @media screen {
24
+ @media screen and (-webkit-min-device-pixel-ratio: 0) {
25
+ input[type="date"].form-control {
26
+ line-height: 16px;
27
+ }
28
+ }
29
+ }
@@ -7,12 +7,11 @@ module Qa::Authorities
7
7
  def search(_q, controller)
8
8
  # The Hyrax::CollectionSearchBuilder expects a current_user
9
9
  return [] unless controller.current_user
10
- response, _docs = search_response(controller)
10
+ response, = search_response(controller)
11
11
  docs = response.documents
12
+
12
13
  docs.map do |doc|
13
- id = doc.id
14
- title = doc.title
15
- { id: id, label: title, value: id }
14
+ { id: doc.id, label: doc.title, value: doc.id }
16
15
  end
17
16
  end
18
17
 
@@ -32,7 +31,7 @@ module Qa::Authorities
32
31
  access = controller.params[:access] || 'read'
33
32
 
34
33
  search_service(controller).search_results do |builder|
35
- builder.where(controller.params[:q])
34
+ builder.with({ q: controller.params[:q] })
36
35
  .with_access(access)
37
36
  .rows(100)
38
37
  end
@@ -33,7 +33,7 @@ module Qa::Authorities
33
33
  access = controller.params[:access] || 'read'
34
34
 
35
35
  search_service(controller).search_results do |builder|
36
- builder.where(controller.params[:q])
36
+ builder.with({ q: controller.params[:q] })
37
37
  .with_access(access)
38
38
  .rows(100)
39
39
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ module BreadcrumbsForCollectionAnalytics
4
+ extend ActiveSupport::Concern
5
+ include Hyrax::Breadcrumbs
6
+
7
+ included do
8
+ before_action :build_breadcrumbs, only: [:index, :show]
9
+ end
10
+
11
+ def add_breadcrumb_for_controller
12
+ add_breadcrumb I18n.t('hyrax.dashboard.breadcrumbs.collections_report'), hyrax.admin_analytics_collection_reports_path
13
+ end
14
+
15
+ def add_breadcrumb_for_action
16
+ case action_name
17
+ when 'show'
18
+ add_breadcrumb params[:id].to_s, hyrax.admin_analytics_collection_reports_path(params[:id]), mark_active_action
19
+ end
20
+ end
21
+
22
+ def mark_active_action
23
+ { "aria-current" => "page" }
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ module BreadcrumbsForWorksAnalytics
4
+ extend ActiveSupport::Concern
5
+ include Hyrax::Breadcrumbs
6
+
7
+ included do
8
+ before_action :build_breadcrumbs, only: [:index, :show]
9
+ end
10
+
11
+ def add_breadcrumb_for_controller
12
+ add_breadcrumb I18n.t('hyrax.dashboard.breadcrumbs.works_report'), hyrax.admin_analytics_work_reports_path
13
+ end
14
+
15
+ def add_breadcrumb_for_action
16
+ case action_name
17
+ when 'show'
18
+ add_breadcrumb params[:id].to_s, hyrax.admin_analytics_work_reports_path(params[:id]), mark_active_action
19
+ end
20
+ end
21
+
22
+ def mark_active_action
23
+ { "aria-current" => "page" }
24
+ end
25
+ end
26
+ end
@@ -29,6 +29,28 @@ module Hyrax::Controller
29
29
  super.merge(locale: I18n.locale)
30
30
  end
31
31
 
32
+ ##
33
+ # @deprecated provides short-term compatibility with Blacklight 6
34
+ # @return [Class<Blacklight::SearchBuilder>]
35
+ def search_builder_class
36
+ return super if defined?(super)
37
+ Deprecation.warn("Avoid direct calls to `#search_builder_class`; this" \
38
+ " method provides short-term compatibility to" \
39
+ " Blacklight 6 clients.")
40
+ blacklight_config.search_builder_class
41
+ end
42
+
43
+ ##
44
+ # @deprecated provides short-term compatibility with Blacklight 6
45
+ # @return [Blacklight::AbstractRepository]
46
+ def repository
47
+ return super if defined?(super)
48
+ Deprecation.warn("Avoid direct calls to `#repository`; this method" \
49
+ " provides short-term compatibility to Blacklight 6 " \
50
+ " clients.")
51
+ blacklight_config.repository
52
+ end
53
+
32
54
  private
33
55
 
34
56
  ##
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ module Admin
4
+ module Analytics
5
+ class AnalyticsController < ApplicationController
6
+ include Hyrax::SingularSubresourceController
7
+ before_action :set_months
8
+ before_action :set_date_range
9
+ before_action :set_document, only: [:show]
10
+ with_themed_layout 'dashboard'
11
+
12
+ def set_document
13
+ @document = ::SolrDocument.find(params[:id])
14
+ end
15
+
16
+ def set_months
17
+ @month_names = 12.downto(1).map { |n| DateTime::ABBR_MONTHNAMES.drop(1)[(Time.zone.today.month - n) % 12] }.reverse
18
+ end
19
+
20
+ def set_date_range
21
+ @start_date = params[:start_date] || Hyrax.config.analytics_start_date
22
+ @end_date = params[:end_date] || Time.zone.today + 1.day
23
+ end
24
+
25
+ def date_range
26
+ "#{@start_date},#{@end_date}"
27
+ end
28
+
29
+ def paginate(results_array, rows: 10)
30
+ return if results_array.nil?
31
+
32
+ total_pages = (results_array.size.to_f / rows.to_f).ceil
33
+ page = request.params[:page].nil? ? 1 : request.params[:page].to_i
34
+ current_page = page > total_pages ? total_pages : page
35
+ Kaminari.paginate_array(results_array, total_count: results_array.size).page(current_page).per(rows)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ module Admin
4
+ module Analytics
5
+ class CollectionReportsController < AnalyticsController
6
+ include Hyrax::BreadcrumbsForCollectionAnalytics
7
+ def index
8
+ return unless Hyrax.config.analytics?
9
+
10
+ @pageviews = Hyrax::Analytics.daily_events('collection-page-view')
11
+ @work_page_views = Hyrax::Analytics.daily_events('work-in-collection-view')
12
+ @downloads = Hyrax::Analytics.daily_events('work-in-collection-download')
13
+ @all_top_collections = Hyrax::Analytics.top_events('work-in-collection-view', date_range)
14
+ @top_collections = paginate(@all_top_collections, rows: 10)
15
+ @top_downloads = Hyrax::Analytics.top_events('work-in-collection-download', date_range)
16
+ @top_collection_pages = Hyrax::Analytics.top_events('collection-page-view', date_range)
17
+ respond_to do |format|
18
+ format.html
19
+ format.csv { export_data }
20
+ end
21
+ end
22
+
23
+ def show
24
+ return unless Hyrax.config.analytics?
25
+ @document = ::SolrDocument.find(params[:id])
26
+ @pageviews = Hyrax::Analytics.daily_events_for_id(@document.id, 'collection-page-view')
27
+ @work_page_views = Hyrax::Analytics.daily_events_for_id(@document.id, 'work-in-collection-view')
28
+ @uniques = Hyrax::Analytics.unique_visitors_for_id(@document.id)
29
+ @downloads = Hyrax::Analytics.daily_events_for_id(@document.id, 'work-in-collection-download')
30
+ respond_to do |format|
31
+ format.html
32
+ format.csv { export_data }
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ # rubocop:disable Metrics/MethodLength
39
+ def export_data
40
+ csv_row = CSV.generate do |csv|
41
+ csv << ["Name", "ID", "View of Works In Collection", "Downloads of Works In Collection", "Collection Page Views"]
42
+ @all_top_collections.each do |collection|
43
+ document = begin
44
+ ::SolrDocument.find(collection[0])
45
+ rescue
46
+ "Collection deleted"
47
+ end
48
+ download_match = @top_downloads.detect { |a, _b| a == collection[0] }
49
+ download_count = download_match ? download_match[1] : 0
50
+ collection_match = @top_collection_pages.detect { |a, _b| a == collection[0] }
51
+ collection_count = collection_match ? collection_match[1] : 0
52
+ csv << [document, collection[0], collection[1], download_count, collection_count]
53
+ end
54
+ end
55
+ send_data csv_row, filename: "#{@start_date}-#{@end_date}-collections.csv"
56
+ end
57
+ # rubocop:enable Metrics/MethodLength
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+ module Hyrax
3
+ module Admin
4
+ module Analytics
5
+ class WorkReportsController < AnalyticsController
6
+ include Hyrax::BreadcrumbsForWorksAnalytics
7
+
8
+ def index
9
+ return unless Hyrax.config.analytics?
10
+
11
+ @accessible_works ||= accessible_works
12
+ @accessible_file_sets ||= accessible_file_sets
13
+ @works_count = @accessible_works.count
14
+ @top_works = paginate(top_works_list, rows: 10)
15
+ @top_file_set_downloads = paginate(top_files_list, rows: 10)
16
+
17
+ if current_user.ability.admin?
18
+ @pageviews = Hyrax::Analytics.daily_events('work-view')
19
+ @downloads = Hyrax::Analytics.daily_events('file-set-download')
20
+ end
21
+
22
+ respond_to do |format|
23
+ format.html
24
+ format.csv { export_data }
25
+ end
26
+ end
27
+
28
+ def show
29
+ @pageviews = Hyrax::Analytics.daily_events_for_id(@document.id, 'work-view')
30
+ @uniques = Hyrax::Analytics.unique_visitors_for_id(@document.id)
31
+ @downloads = Hyrax::Analytics.daily_events_for_id(@document.id, 'file_set_in_work_download')
32
+ @files = paginate(@document._source["file_set_ids_ssim"], rows: 5)
33
+ respond_to do |format|
34
+ format.html
35
+ format.csv { export_data }
36
+ end
37
+ end
38
+
39
+ private
40
+
41
+ def accessible_works
42
+ models = Hyrax.config.curation_concerns.map { |m| "\"#{m}\"" }
43
+ if current_user.ability.admin?
44
+ ActiveFedora::SolrService.query("has_model_ssim:(#{models.join(' OR ')})",
45
+ fl: 'title_tesim, id, member_of_collections',
46
+ rows: 50_000)
47
+ else
48
+ ActiveFedora::SolrService.query(
49
+ "edit_access_person_ssim:#{current_user} AND has_model_ssim:(#{models.join(' OR ')})",
50
+ fl: 'title_tesim, id, member_of_collections',
51
+ rows: 50_000
52
+ )
53
+ end
54
+ end
55
+
56
+ def accessible_file_sets
57
+ if current_user.ability.admin?
58
+ ActiveFedora::SolrService.query(
59
+ "has_model_ssim:FileSet",
60
+ fl: 'title_tesim, id',
61
+ rows: 50_000
62
+ )
63
+ else
64
+ ActiveFedora::SolrService.query(
65
+ "edit_access_person_ssim:#{current_user} AND has_model_ssim:FileSet",
66
+ fl: 'title_tesim, id',
67
+ rows: 50_000
68
+ )
69
+ end
70
+ end
71
+
72
+ def top_analytics_works
73
+ @top_analytics_works ||= Hyrax::Analytics.top_events('work-view', date_range)
74
+ end
75
+
76
+ def top_analytics_downloads
77
+ @top_analytics_downloads ||= Hyrax::Analytics.top_events('file-set-in-work-download', date_range)
78
+ end
79
+
80
+ def top_analytics_file_sets
81
+ @top_analytics_file_sets ||= Hyrax::Analytics.top_events('file-set-download', date_range)
82
+ end
83
+
84
+ def top_works_list
85
+ list = []
86
+ top_analytics_works
87
+ top_analytics_downloads
88
+ @accessible_works.each do |doc|
89
+ views_match = @top_analytics_works.detect { |id, _count| id == doc["id"] }
90
+ @view_count = views_match ? views_match[1] : 0
91
+ downloads_match = @top_analytics_downloads.detect { |id, _count| id == doc["id"] }
92
+ @download_count = downloads_match ? downloads_match[1] : 0
93
+ list.push([doc["id"], doc["title_tesim"].join(''), @view_count, @download_count, doc["member_of_collections"]])
94
+ end
95
+ list.sort_by { |l| -l[2] }
96
+ end
97
+
98
+ def top_files_list
99
+ list = []
100
+ top_analytics_file_sets
101
+ @accessible_file_sets.each do |doc|
102
+ downloads_match = @top_analytics_file_sets.detect { |id, _count| id == doc["id"] }
103
+ @download_count = downloads_match ? downloads_match[1] : 0
104
+ list.push([doc["id"], doc["title_tesim"].join(''), @download_count]) if doc["title_tesim"].present?
105
+ end
106
+ list.sort_by { |l| -l[2] }
107
+ end
108
+
109
+ def export_data
110
+ data = top_works_list
111
+ csv_row = CSV.generate do |csv|
112
+ csv << ["Name", "ID", "Work Page Views", "Total Downloads of File Sets In Work", "Collections"]
113
+ data.each do |d|
114
+ csv << [d[0], d[1], d[2], d[3], d[4]]
115
+ end
116
+ end
117
+ send_data csv_row, filename: "#{params[:start_date]}-#{params[:end_date]}-works.csv"
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -4,10 +4,13 @@ module Hyrax
4
4
  include CollectionsControllerBehavior
5
5
  include BreadcrumbsForCollections
6
6
  with_themed_layout :decide_layout
7
- load_and_authorize_resource except: [:index, :create],
7
+ load_and_authorize_resource except: [:index],
8
8
  instance_name: :collection,
9
9
  class: Hyrax.config.collection_model
10
10
 
11
+ skip_load_resource only: :create if
12
+ Hyrax.config.collection_class < ActiveFedora::Base
13
+
11
14
  # Renders a JSON response with a list of files in this collection
12
15
  # This is used by the edit form to populate the thumbnail_id dropdown
13
16
  def files
@@ -86,11 +86,13 @@ module Hyrax
86
86
  end
87
87
 
88
88
  def after_create
89
- form
90
- set_default_permissions
91
- # if we are creating the new collection as a subcollection (via the nested collections controller),
92
- # we pass the parent_id through a hidden field in the form and link the two after the create.
93
- link_parent_collection(params[:parent_id]) unless params[:parent_id].nil?
89
+ if @collection.is_a?(ActiveFedora::Base)
90
+ form
91
+ set_default_permissions
92
+ # if we are creating the new collection as a subcollection (via the nested collections controller),
93
+ # we pass the parent_id through a hidden field in the form and link the two after the create.
94
+ link_parent_collection(params[:parent_id]) unless params[:parent_id].nil?
95
+ end
94
96
  respond_to do |format|
95
97
  Hyrax::SolrService.commit
96
98
  format.html { redirect_to edit_dashboard_collection_path(@collection), notice: t('hyrax.dashboard.my.action.collection_create_success') }
@@ -220,11 +222,13 @@ module Hyrax
220
222
  @collection = transactions['change_set.create_collection']
221
223
  .with_step_args(
222
224
  'change_set.set_user_as_depositor' => { user: current_user },
225
+ 'change_set.add_to_collections' => { collection_ids: Array(params[:parent_id]) },
223
226
  'collection_resource.apply_collection_type_permissions' => { user: current_user }
224
227
  )
225
228
  .call(form)
226
229
  .value_or { return after_create_error }
227
230
 
231
+ after_create
228
232
  add_members_to_collection unless batch.empty?
229
233
  @collection
230
234
  end
@@ -241,6 +245,10 @@ module Hyrax
241
245
  Hyrax::CollectionType.find_or_create_default_collection_type
242
246
  end
243
247
 
248
+ def default_collection_type_gid
249
+ default_collection_type.to_global_id.to_s
250
+ end
251
+
244
252
  def collection_type
245
253
  @collection_type ||= CollectionType.find_by_gid!(collection.collection_type_gid)
246
254
  end
@@ -390,7 +398,8 @@ module Hyrax
390
398
  form_class.model_attributes(params[:collection])
391
399
  else
392
400
  params.permit(collection: {})[:collection]
393
- .merge(params.permit(:collection_type_gid))
401
+ .merge(params.permit(:collection_type_gid)
402
+ .with_defaults(collection_type_gid: default_collection_type_gid))
394
403
  .merge(member_of_collection_ids: Array(params[:parent_id]))
395
404
  end
396
405
  end
@@ -6,6 +6,7 @@ module Hyrax
6
6
  with_themed_layout 'dashboard'
7
7
  before_action :authenticate_user!
8
8
  before_action :build_breadcrumbs, only: [:show]
9
+ before_action :set_date_range
9
10
 
10
11
  ##
11
12
  # @!attribute [rw] sidebar_partials
@@ -26,5 +27,12 @@ module Hyrax
26
27
  render 'show_user'
27
28
  end
28
29
  end
30
+
31
+ private
32
+
33
+ def set_date_range
34
+ @start_date = params[:start_date] || Time.zone.today - 1.month
35
+ @end_date = params[:end_date] || Time.zone.today + 1.day
36
+ end
29
37
  end
30
38
  end
@@ -7,7 +7,9 @@ module Hyrax
7
7
  before_action :build_breadcrumbs, only: [:work, :file]
8
8
 
9
9
  def work
10
- @stats = Hyrax::WorkUsage.new(params[:id])
10
+ @document = ::SolrDocument.find(params[:id])
11
+ @pageviews = Hyrax::Analytics.daily_events_for_id(@document.id, 'work-view')
12
+ @downloads = Hyrax::Analytics.daily_events_for_id(@document.id, 'file-set-in-work-download')
11
13
  end
12
14
 
13
15
  def file
@@ -17,6 +17,9 @@ module Hyrax
17
17
 
18
18
  property :member_of_collection_ids, default: [], type: Valkyrie::Types::Array
19
19
 
20
+ validates :title, presence: true
21
+ validates :collection_type_gid, presence: true
22
+
20
23
  class << self
21
24
  def model_class
22
25
  Hyrax::PcdmCollection
@@ -37,7 +37,7 @@ module Hyrax
37
37
  solr_doc['file_format_tesim'] = file_format(file_metadata)
38
38
  solr_doc['file_format_sim'] = file_format(file_metadata)
39
39
  solr_doc['file_size_lts'] = file_metadata.size[0]
40
- solr_doc['type_tesim'] = file_metadata.type if file_metadata.type.present?
40
+ solr_doc['type_tesim'] = file_metadata.type.map(&:to_s) if file_metadata.type.present?
41
41
 
42
42
  # attributes set by fits
43
43
  solr_doc['format_label_tesim'] = file_metadata.format_label if file_metadata.format_label.present?
@@ -1,7 +1,27 @@
1
1
  # frozen_string_literal: true
2
+
3
+ ##
4
+ # a +ActiveJob+ job to process file characterization.
5
+ #
6
+ # the characterization process is handled by a service object, which is
7
+ # configurable via {CharacterizeJob.characterization_service}.
8
+ #
9
+ # @example setting a custom characterization service
10
+ # class MyCharacterizer
11
+ # def run(file, path)
12
+ # # do custom characterization
13
+ # end
14
+ # end
15
+ #
16
+ # # in a Rails initializer
17
+ # CharacterizeJob.characterization_service = MyCharacterizer.new
18
+ # end
2
19
  class CharacterizeJob < Hyrax::ApplicationJob
3
20
  queue_as Hyrax.config.ingest_queue_name
4
21
 
22
+ class_attribute :characterization_service
23
+ self.characterization_service = Hydra::Works::CharacterizationService
24
+
5
25
  # Characterizes the file at 'filepath' if available, otherwise, pulls a copy from the repository
6
26
  # and runs characterization on that file.
7
27
  # @param [FileSet] file_set
@@ -17,7 +37,7 @@ class CharacterizeJob < Hyrax::ApplicationJob
17
37
  private
18
38
 
19
39
  def characterize(file_set, _file_id, filepath)
20
- Hydra::Works::CharacterizationService.run(file_set.characterization_proxy, filepath)
40
+ characterization_service.run(file_set.characterization_proxy, filepath)
21
41
  Rails.logger.debug "Ran characterization on #{file_set.characterization_proxy.id} (#{file_set.characterization_proxy.mime_type})"
22
42
  file_set.characterization_proxy.alpha_channels = channels(filepath) if file_set.image? && Hyrax.config.iiif_image_server?
23
43
  file_set.characterization_proxy.save!
@@ -31,4 +51,11 @@ class CharacterizeJob < Hyrax::ApplicationJob
31
51
  end
32
52
  [ch]
33
53
  end
54
+
55
+ ##
56
+ # @api public
57
+ # @return [#run]
58
+ def characterization_service
59
+ self.class.characterization_service
60
+ end
34
61
  end