From 368c217047a73bbe9a6478beb14d93d6b31a779e Mon Sep 17 00:00:00 2001 From: Alin Vetian Date: Mon, 15 Jul 2024 15:38:48 +0300 Subject: [PATCH 01/14] added new deletion reminders, added letter_opener --- Gemfile | 4 +- Gemfile.lock | 9 + app/mailers/stash_engine/resource_mailer.rb | 77 ++++++ app/models/stash_engine/curation_activity.rb | 4 +- .../delete_notifications_service.rb | 101 ++++++++ ...tion_required_delete_notification.html.erb | 19 ++ .../in_progress_delete_notification.html.erb | 19 ++ .../peer_review_delete_notification.html.erb | 17 ++ config/environments/local.rb | 20 +- config/routes.rb | 92 +++---- ...0240711144223_populate_last_status_date.rb | 22 ++ db/data_schema.rb | 2 +- ...1_add_last_status_date_to_process_dates.rb | 6 + lib/tasks/dataset_deletion_tasks.rake | 24 ++ .../delete_notifications_service_spec.rb | 242 ++++++++++++++++++ 15 files changed, 594 insertions(+), 64 deletions(-) create mode 100644 app/mailers/stash_engine/resource_mailer.rb create mode 100644 app/services/stash_engine/delete_notifications_service.rb create mode 100644 app/views/stash_engine/resource_mailer/action_required_delete_notification.html.erb create mode 100644 app/views/stash_engine/resource_mailer/in_progress_delete_notification.html.erb create mode 100644 app/views/stash_engine/resource_mailer/peer_review_delete_notification.html.erb create mode 100644 db/data/20240711144223_populate_last_status_date.rb create mode 100644 db/migrate/20240711144031_add_last_status_date_to_process_dates.rb create mode 100644 lib/tasks/dataset_deletion_tasks.rake create mode 100644 spec/services/stash_engine/delete_notifications_service_spec.rb diff --git a/Gemfile b/Gemfile index 78e5e12478..c8f35e68a4 100644 --- a/Gemfile +++ b/Gemfile @@ -176,7 +176,7 @@ group :test do gem 'webmock' end -group :development, :test, :local_dev do +group :development, :test, :local_dev, :local do gem 'binding_of_caller' # Ruby fast debugger - base + CLI (http://github.com/deivid-rodriguez/byebug) gem 'byebug' @@ -188,4 +188,6 @@ group :development, :test, :local_dev do gem 'spring' # rspec command for spring (https://github.com/jonleighton/spring-commands-rspec) gem 'spring-commands-rspec' + gem 'letter_opener' + gem 'letter_opener_web', '~> 3.0' end diff --git a/Gemfile.lock b/Gemfile.lock index 475d1e7053..b867218f7e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -418,6 +418,13 @@ GEM addressable (~> 2.8) leaflet-rails (1.7.0) rails (>= 4.2.0) + letter_opener (1.10.0) + launchy (>= 2.2, < 4) + letter_opener_web (3.0.0) + actionmailer (>= 6.1) + letter_opener (~> 1.9) + railties (>= 6.1) + rexml listen (3.8.0) rb-fsevent (~> 0.10, >= 0.10.3) rb-inotify (~> 0.9, >= 0.9.10) @@ -866,6 +873,8 @@ DEPENDENCIES jwt (~> 2.3.0) kaminari (~> 1.2.2) leaflet-rails (~> 1.7.0) + letter_opener + letter_opener_web (~> 3.0) listen loofah (~> 2.19.1) mail (~> 2.8) diff --git a/app/mailers/stash_engine/resource_mailer.rb b/app/mailers/stash_engine/resource_mailer.rb new file mode 100644 index 0000000000..f747ad447d --- /dev/null +++ b/app/mailers/stash_engine/resource_mailer.rb @@ -0,0 +1,77 @@ +module StashEngine + + # Mails users about submissions + class ResourceMailer < ApplicationMailer + + def in_progress_delete_notification(resource) + logger.warn('Unable to send in_progress_delete_notification; nil resource') unless resource.present? + return unless resource.present? + + assign_variables(resource) + return unless @user.present? && user_email(@user).present? + + mail(to: user_email(@user), + subject: "#{rails_env}REMINDER: Dryad Submission \"#{@resource.title}\"") + + end + + def peer_review_delete_notification(resource) + logger.warn('Unable to send peer_review_delete_notification; nil resource') unless resource.present? + return unless resource.present? + + assign_variables(resource) + return unless @user.present? && user_email(@user).present? + + mail(to: user_email(@user), + subject: "#{rails_env}REMINDER: Dryad Submission \"#{@resource.title}\"") + end + + def action_required_delete_notification(resource) + logger.warn('Unable to send action_required_delete_notification; nil resource') unless resource.present? + return unless resource.present? + + assign_variables(resource) + return unless @user.present? && user_email(@user).present? + + mail(to: user_email(@user), + subject: "#{rails_env}REMINDER: Dryad Submission \"#{@resource.title}\"") + end + + private + + # rubocop:disable Style/NestedTernaryOperator + def user_email(user) + user.present? ? (user.respond_to?(:author_email) ? user.author_email : user.email) : nil + end + + def user_name(user) + user.present? ? (user.respond_to?(:author_standard_name) ? user.author_standard_name : user.name) : nil + end + # rubocop:enable Style/NestedTernaryOperator + + def assign_variables(resource) + @resource = resource + @user = resource.owner_author || resource.user + @user_name = user_name(@user) + @helpdesk_email = APP_CONFIG['helpdesk_email'] || 'help@datadryad.org' + @bcc_emails = APP_CONFIG['submission_bc_emails'] || [@helpdesk_email] + @submission_error_emails = APP_CONFIG['submission_error_email'] || [@helpdesk_email] + @page_error_emails = APP_CONFIG['page_error_email'] || [@helpdesk_email] + end + + + + def rails_env + return "[#{Rails.env}] " unless Rails.env.include?('production') + + '' + end + + def address_list(addresses) + addresses = [addresses] unless addresses.respond_to?(:join) + addresses.flatten.reject(&:blank?).join(',') + end + + end + +end diff --git a/app/models/stash_engine/curation_activity.rb b/app/models/stash_engine/curation_activity.rb index 8fd0a14ede..096e8a74be 100644 --- a/app/models/stash_engine/curation_activity.rb +++ b/app/models/stash_engine/curation_activity.rb @@ -92,7 +92,7 @@ class CurationActivity < ApplicationRecord # rubocop:disable Metrics/ClassLength (ca.published? || ca.embargoed?) && curation_status_changed? } - after_create :process_dates, if: %i[curation_status_changed? first_time_in_status?] + after_create :process_dates, if: %i[curation_status_changed?] # the publication flags need to be set before creating datacite metadata (after create below) after_create :update_publication_flags, if: proc { |ca| %w[published embargoed peer_review withdrawn].include?(ca.status) } @@ -227,7 +227,7 @@ def update_solr end def process_dates - update_dates = {} + update_dates = {last_status_date: Time.current} if first_time_in_status? case status when 'processing', 'peer_review', 'submitted', 'withdrawn' diff --git a/app/services/stash_engine/delete_notifications_service.rb b/app/services/stash_engine/delete_notifications_service.rb new file mode 100644 index 0000000000..abdedfb811 --- /dev/null +++ b/app/services/stash_engine/delete_notifications_service.rb @@ -0,0 +1,101 @@ +module StashEngine + + class DeleteNotificationsService + attr_reader :logging + + def initialize(logging = false) + @logging = logging + end + + # Send In Progress delete email notification + # - email is sent monthly starting from first month until 1 year + # - after 1 year the resource should get deleted + def send_in_progress_reminders + StashEngine::Resource.latest_per_dataset.joins(:last_curation_activity).joins(:process_date) + .where(stash_engine_curation_activities: { status: 'in_progress' }) + .where(stash_engine_process_dates: { last_status_date: 1.year.ago.beginning_of_day..1.months.ago.end_of_day }) + .each do |resource| + reminder_flag = 'in_progress_deletion_notice' + last_reminder = resource.curation_activities.where('note LIKE ?', "%#{reminder_flag}%")&.last + if resource.current_curation_status == 'in_progress' && + resource.identifier.latest_resource_id == resource.id && + (last_reminder.blank? || last_reminder.created_at <= 1.month.ago) + + log("Mailing submitter about deletion of in_progress dataset. Identifier: #{resource.identifier_id}, Resource: #{resource.id} updated #{resource.updated_at}") + StashEngine::ResourceMailer.in_progress_delete_notification(resource).deliver_now + create_activity(reminder_flag, resource) + end + return true + rescue StandardError => e + p " Exception! #{e.message}" + end + end + + # Send Action Required delete email notification + # - email is sent monthly starting from first month until 1 year + # - after 1 year the resource should be set to Withdrawn status + def send_action_required_reminders + StashEngine::Resource.latest_per_dataset.joins(:last_curation_activity).joins(:process_date) + .where(stash_engine_curation_activities: { status: 'action_required' }) + .where(stash_engine_process_dates: { last_status_date: 1.year.ago.beginning_of_day..1.months.ago.end_of_day }) + .each do |resource| + + reminder_flag = 'action_required_deletion_notice' + last_reminder = resource.curation_activities.where('note LIKE ?', "%#{reminder_flag}%")&.last + + if resource.current_curation_status == 'action_required' && + resource.identifier.latest_resource_id == resource.id && + (last_reminder.blank? || last_reminder.created_at <= 1.month.ago) + + log resource.user.email + log("Mailing submitter about deletion of action_required dataset. Identifier: #{resource.identifier_id}, Resource: #{resource.id} updated #{resource.updated_at}") + StashEngine::ResourceMailer.action_required_delete_notification(resource).deliver_now + create_activity(reminder_flag, resource) + end + return true + rescue StandardError => e + p " Exception! #{e.message}" + end + end + + # Send Peer Review delete email notification + # - email is sent monthly starting from 6th month until 1 year + # - after 1 year the resource should be set to Withdrawn status + def send_peer_review_reminders + StashEngine::Resource.latest_per_dataset.joins(:last_curation_activity).joins(:process_date) + .where(stash_engine_curation_activities: { status: 'peer_review' }) + .where(stash_engine_process_dates: { last_status_date: 1.year.ago.beginning_of_day..6.months.ago.end_of_day }) + .each do |resource| + + reminder_flag = 'peer_review_deletion_notice' + last_reminder = resource.curation_activities.where('note LIKE ?', "%#{reminder_flag}%")&.last + if last_reminder.blank? || last_reminder.created_at <= 1.month.ago + log("Mailing submitter about deletion of peer_review dataset. Identifier: #{resource.identifier_id}, Resource: #{resource.id} updated #{resource.updated_at}") + StashEngine::ResourceMailer.peer_review_delete_notification(resource).deliver_now + create_activity(reminder_flag, resource) + end + rescue StandardError => e + p " Exception! #{e.message}" + end + true + end + + private + + def create_activity(flag, resource) + status = resource.last_curation_activity.status + StashEngine::CurationActivity.create( + resource_id: resource.id, + user_id: 0, + status: status, + note: "#{flag} - reminded submitter that this item is still `#{status}`" + ) + end + + def log(message) + return unless logging + + p message + end + end +end diff --git a/app/views/stash_engine/resource_mailer/action_required_delete_notification.html.erb b/app/views/stash_engine/resource_mailer/action_required_delete_notification.html.erb new file mode 100644 index 0000000000..90485fb165 --- /dev/null +++ b/app/views/stash_engine/resource_mailer/action_required_delete_notification.html.erb @@ -0,0 +1,19 @@ +

Dear <%= @user_name %>,

+ +

+ Previously, we requested modifications to your Dryad data submission. As a reminder, these changes + must be addressed before we can publish your dataset: +

+ +

+ Title: "<%= @resource.title %>"
+ DOI: <%= @resource.identifier_str %> +

+ +

+ Our previous email correspondence contains specific details on the changes required. Please contact us at help@datadryad.org if you have any questions or no longer wish to proceed with the publication of the dataset. We will send you another reminder in about 2 weeks. +

+ +

We hope to hear from you shortly.

+ +

The Dryad Team

diff --git a/app/views/stash_engine/resource_mailer/in_progress_delete_notification.html.erb b/app/views/stash_engine/resource_mailer/in_progress_delete_notification.html.erb new file mode 100644 index 0000000000..5d2156c3e2 --- /dev/null +++ b/app/views/stash_engine/resource_mailer/in_progress_delete_notification.html.erb @@ -0,0 +1,19 @@ +

Dear <%= @user_name %>,

+ +

Your Dryad data submission, “<%= @resource.title %>”, is currently "In Progress". It has not yet been submitted for evaluation by a member of our curation team.

+ +

When you are prepared to submit this dataset for curation and publication, please follow these steps:

+
    +
  1. Login to https://datadryad.org
  2. +
  3. Click on "My datasets"
  4. +
  5. Locate your submission with the status "In Progress"
  6. +
  7. Click "Resume" to access your submission
  8. +
  9. Incorporate pending edits, if any
  10. +
  11. Click "Submit"
  12. +
+ +

If you do not intend to proceed with the publication of this dataset, you may either select "Delete" from your dashboard if this option is available to you, or contact Dryad's Helpdesk at <%= @helpdesk_email %> to request that it is withdrawn. + +

Should you need to reach out to us in regards to this submission, please include the DOI in your email to ensure swift assistance.

+ +

The Dryad Team

diff --git a/app/views/stash_engine/resource_mailer/peer_review_delete_notification.html.erb b/app/views/stash_engine/resource_mailer/peer_review_delete_notification.html.erb new file mode 100644 index 0000000000..9a06c20f4b --- /dev/null +++ b/app/views/stash_engine/resource_mailer/peer_review_delete_notification.html.erb @@ -0,0 +1,17 @@ +

Dear <%= @user_name %>,

+ +

Title: <%= @resource.title %>
+ DOI: <%= @resource.identifier_str %>

+ +

As a reminder, your Dryad data submission is currently in "Private for peer review" status and has not entered our curation process. If your related manuscript is still in the peer review process, you may ignore this email.

+ +

If your related article has been accepted, or if you are ready to initiate the curation process and proceed to publication, please follow these steps:

+
    +
  1. Login to https://datadryad.org
  2. +
  3. Click on "My datasets" to view your list of data submissions.
  4. +
  5. Click "Release for curation" on your dataset.
  6. +
+ +

If your related article has been rejected, or you will not be proceeding with the submission of this dataset, kindly contact us at <%= @helpdesk_email %>, and we will withdraw your submission.

+ +

The Dryad Team

diff --git a/config/environments/local.rb b/config/environments/local.rb index c3db9f97da..c7009b5578 100644 --- a/config/environments/local.rb +++ b/config/environments/local.rb @@ -34,8 +34,8 @@ config.active_record.migration_error = :page_load # Store uploaded files on the local file system (see config/storage.yml for options) - # config.active_storage.service = :local - + # config.active_storage.service = :local + # Debug mode disables concatenation and preprocessing of assets. # This option may cause significant delays in view rendering with a large # number of complex assets. @@ -53,20 +53,10 @@ # Raises error for missing translations # config.action_view.raise_on_missing_translations = true - # Email through Amazon SES - # Although it would be nice to read these settings from the APP_CONFIG, - # that hash doesn't exist at the time this file is loaded, so we need to - # put the configuration directly in here. - ActionMailer::Base.smtp_settings = { - :address => 'email-smtp.us-west-2.amazonaws.com', - :port => '587', - :authentication => :plain, - :user_name => 'AKIA2KERHV5ERJHPR552', - :password => Rails.application.credentials[Rails.env.to_sym][:aws_ses_password] - } - - config.action_mailer.perform_deliveries = true + # Do not email users config.action_mailer.raise_delivery_errors = true + config.action_mailer.delivery_method = :letter_opener + config.action_mailer.perform_deliveries = true Rails.application.default_url_options = { host: 'localhost', port: 3000} end diff --git a/config/routes.rb b/config/routes.rb index a63cd60c1b..395c66d172 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -8,6 +8,8 @@ root :requirements => { :protocol => 'http' }, :to => redirect(path: APP_CONFIG.stash_mount ) + mount LetterOpenerWeb::Engine, at: "/letter_opener" if Rails.env.local? + # Example of regular route: # get 'products/:id' => 'catalog#view' @@ -90,7 +92,7 @@ match '/search', to: 'datasets#search', via: %i[get] get '/reports', to: 'api#reports_index' get '/reports(/:report_name)', to: 'api#reports' - + # Support for the Editorial Manager API match '/em_submission_metadata(/:id)', constraints: { id: /\S+/ }, to: 'datasets#em_submission_metadata', via: %i[post put] @@ -113,16 +115,16 @@ end resources :processor_results, only: [:show, :index, :create, :update] end - + resources :urls, shallow: true, path: '/urls', only: [:create] end - + # this one doesn't follow the pattern since it gloms filename on the end, so manual route # supporting both POST and PUT for updating the file to ensure as many clients as possible can use this end point match '/datasets/:id/files/:filename', to: 'files#update', as: 'dataset_file', constraints: { id: %r{[^\s/]+?}, filename: %r{[^\s/]+?} }, format: false, via: %i[post put] resources :users, path: '/users', only: %i[index show] - + get '/queue_length', to: 'submission_queue#length' end @@ -136,7 +138,7 @@ ########################## StashEngine support ###################################### - scope module: 'stash_engine', path: '/stash' do + scope module: 'stash_engine', path: '/stash' do get 'landing/show' @@ -159,21 +161,21 @@ end match 'identifier_internal_data/:identifier_id', to: 'internal_data#create', as: 'internal_data_create', via: %i[get post put] resources :internal_data, shallow: true, as: 'stash_engine_internal_data' - + resources :tenants, only: %i[index show] resources :data_files, :software_files, :supp_files do member do patch 'destroy_manifest' # destroy file from manifest method end end - + resources :edit_histories, only: [:index] # these are weird and different and want to get rid of these with file redesign match 'data_file/validate_urls/:resource_id', to: 'data_files#validate_urls', as: 'data_file_validate_urls', via: %i[get post put] match 'software_file/validate_urls/:resource_id', to: 'software_files#validate_urls', as: 'software_file_validate_urls', via: %i[get post put] match 'supp_file/validate_urls/:resource_id', to: 'supp_files#validate_urls', as: 'supp_file_validate_urls', via: %i[get post put] - + get 'data_file/presign_upload/:resource_id', to: 'data_files#presign_upload', as: 'data_file_presign_url' get 'software_file/presign_upload/:resource_id', to: 'software_files#presign_upload', as: 'software_file_presign_url' get 'supp_file/presign_upload/:resource_id', to: 'supp_files#presign_upload', as: 'supp_file_presign_url' @@ -191,7 +193,7 @@ get 'generic_file/check_frictionless/:resource_id', to: 'generic_files#check_frictionless', as: 'generic_file_check_frictionless' - + get 'dashboard', to: 'dashboard#show', as: 'dashboard' get 'dashboard/user_datasets', to: 'dashboard#user_datasets' get 'ajax_wait', to: 'dashboard#ajax_wait', as: 'ajax_wait' @@ -199,7 +201,7 @@ get 'preparing_to_submit', to: 'dashboard#preparing_to_submit', as: 'preparing_to_submit' get 'upload_basics', to: 'dashboard#upload_basics', as: 'upload_basics' get 'react_basics', to: 'dashboard#react_basics', as: 'react_basics' - + # download related match 'downloads/zip_assembly_info/:resource_id', to: 'downloads#zip_assembly_info', as: 'zip_assembly_info', via: %i[get post] match 'downloads/download_resource/:resource_id', to: 'downloads#download_resource', as: 'download_resource', via: %i[get post] @@ -209,31 +211,31 @@ get 'downloads/preview_csv/:file_id', to: 'downloads#preview_csv', as: 'preview_csv' get 'share/:id', to: 'downloads#share', as: 'share' get 'downloads/assembly_status/:id', to: 'downloads#assembly_status', as: 'download_assembly_status' - + get 'edit/:doi/:edit_code', to: 'metadata_entry_pages#edit_by_doi', as: 'edit', constraints: { doi: /\S+/ } match 'metadata_entry_pages/find_or_create', to: 'metadata_entry_pages#find_or_create', via: %i[get post put] match 'metadata_entry_pages/new_version', to: 'metadata_entry_pages#new_version', via: %i[post get] post 'metadata_entry_pages/new_version_from_previous', to: 'metadata_entry_pages#new_version_from_previous' match 'metadata_entry_pages/reject_agreement', to: 'metadata_entry_pages#reject_agreement', via: [:delete] match 'metadata_entry_pages/accept_agreement', to: 'metadata_entry_pages#accept_agreement', via: [:post] - + # root 'sessions#index' root 'pages#home', as: 'pages_root' - + match 'auth/orcid/callback', to: 'sessions#orcid_callback', via: %i[get post] match 'auth/google_oauth2/callback', to: 'sessions#google_callback', via: %i[get post] match 'auth/developer/callback', to: 'sessions#developer_callback', via: %i[get post] match 'auth/:provider/callback', to: 'sessions#callback', via: %i[get post] match 'session/test_login', to: 'sessions#test_login', via: [:get, :post], as: 'test_login' - + match 'terms/view', to: 'dashboard#view_terms', via: %i[get post] - + get 'auth/failure', to: redirect('/') match 'sessions/destroy', to: 'sessions#destroy', via: %i[get post] get 'sessions/choose_login', to: 'sessions#choose_login', as: 'choose_login' get 'sessions/choose_sso', to: 'sessions#choose_sso', as: 'choose_sso' match 'sessions/no_partner', to: 'sessions#no_partner', as: 'no_partner', via: [:get, :post] - post 'sessions/sso', to: 'sessions#sso', as: 'sso' + post 'sessions/sso', to: 'sessions#sso', as: 'sso' get 'feedback', to: 'sessions#feedback', as: 'feedback' post 'feedback_signup', to: 'sessions#feedback_signup', as: 'feedback_signup' @@ -274,14 +276,14 @@ get 'landing/metrics/:identifier_id', to: 'landing#metrics', as: 'show_metrics' get 'test', to: 'pages#test' get 'ip_error', to: 'pages#ip_error' - + # user management get 'account', to: 'user_account#index', as: 'my_account' post 'account/edit', to: 'user_account#edit', as: 'edit_account' # admin user management get 'user_admin', to: 'user_admin#index' # main page for administering users # page for viewing a single user - get 'user_admin/user_profile/:id', to: 'user_admin#user_profile', as: 'user_admin_profile' + get 'user_admin/user_profile/:id', to: 'user_admin#user_profile', as: 'user_admin_profile' post 'user_admin/set_role/:id', to: 'user_admin#set_role', as: 'user_admin_set_role' # admin editing user get 'user_admin/merge', to: 'user_admin#merge_popup', as: 'user_merge_popup' @@ -329,19 +331,19 @@ # admin report for dataset funders get 'ds_admin_funders', to: 'admin_dataset_funders#index', as: 'ds_admin_funders' - + # routing for submission queue controller get 'submission_queue', to: 'submission_queue#index' get 'submission_queue/refresh_table', to: 'submission_queue#refresh_table' get 'submission_queue/graceful_start', to: 'submission_queue#graceful_start', as: 'graceful_start' - + # routing for zenodo_queue get 'zenodo_queue', to: 'zenodo_queue#index', as: 'zenodo_queue' get 'zenodo_queue/item_details/:id', to: 'zenodo_queue#item_details', as: 'zenodo_queue_item_details' get 'zenodo_queue/identifier_details/:id', to: 'zenodo_queue#identifier_details', as: 'zenodo_queue_identifier_details' post 'zenodo_queue/resubmit_job', to: 'zenodo_queue#resubmit_job', as: 'zenodo_queue_resubmit_job' post 'zenodo_queue/set_errored', to: 'zenodo_queue#set_errored', as: 'zenodo_queue_set_errored' - + # Administrative Status Dashboard that displays statuses of external dependencies get 'status_dashboard', to: 'status_dashboard#show' @@ -349,10 +351,10 @@ get 'publication_updater', to: 'publication_updater#index' put 'publication_updater/:id', to: 'publication_updater#update' delete 'publication_updater/:id', to: 'publication_updater#destroy' - + # Curation stats get 'curation_stats', to: 'curation_stats#index' - + # Journals get 'journals', to: 'journals#index' @@ -375,33 +377,33 @@ # this is kind of hacky, but it directs our search results to open links to the landing pages resources :solr_documents, only: [:show], path: '/stash/dataset', controller: 'catalog' - + ########################## StashDatacite support ###################################### - scope module: 'stash_datacite', path: '/stash_datacite' do + scope module: 'stash_datacite', path: '/stash_datacite' do get 'titles/new', to: 'titles#new' post 'titles/create', to: 'titles#create' patch 'titles/update', to: 'titles#update' - + get 'descriptions/new', to: 'descriptions#new' patch 'descriptions/update', to: 'descriptions#update' - + get 'temporal_coverages/new', to: 'temporal_coverages#new' patch 'temporal_coverages/update', to: 'temporal_coverages#update' - + get 'authors/new', to: 'authors#new' post 'authors/create', to: 'authors#create' patch 'authors/update', to: 'authors#update' delete 'authors/:id/delete', to: 'authors#delete', as: 'authors_delete' patch 'authors/reorder', to: 'authors#reorder', as: 'authors_reorder' - + get 'contributors/new', to: 'contributors#new' get 'contributors/autocomplete', to: 'contributors#autocomplete' post 'contributors/create', to: 'contributors#create' patch 'contributors/update', to: 'contributors#update' patch 'contributors/reorder', to: 'contributors#reorder', as: 'contributors_reorder' delete 'contributors/:id/delete', to: 'contributors#delete', as: 'contributors_delete' - + get 'publications/new', to: 'publications#new' get 'publications/autocomplete', to: 'publications#autocomplete' get 'publications/issn/:id', to: 'publications#issn' @@ -409,46 +411,46 @@ patch 'publications/update', to: 'publications#update' delete 'publications/:id/delete', to: 'publications#delete', as: 'publications_delete' post 'publications/autofill/:id', to: 'publications#autofill_data', as: 'publications_autofill_data' - + get 'resource_types/new', to: 'resource_types#new' post 'resource_types/create', to: 'resource_types#create' patch 'resource_types/update', to: 'resource_types#update' - + get 'subjects/new', to: 'subjects#new' get 'subjects/autocomplete', to: 'subjects#autocomplete' post 'subjects/create', to: 'subjects#create' delete 'subjects/:id/delete', to: 'subjects#delete', as: 'subjects_delete' get 'subjects/landing', to: 'subjects#landing', as: 'subjects_landing' - + # fos subjects are a special subject that is treated differently for the OECD Field of Science patch 'fos_subjects/update', to: 'fos_subjects#update' - + get 'related_identifiers/new', to: 'related_identifiers#new' post 'related_identifiers/create', to: 'related_identifiers#create' patch 'related_identifiers/update', to: 'related_identifiers#update' delete 'related_identifiers/:id/delete', to: 'related_identifiers#delete', as: 'related_identifiers_delete' get 'related_identifiers/report', to: 'related_identifiers#report', as: 'related_identifiers_report' get 'related_identifiers/show', to: 'related_identifiers#show', as: 'related_identifiers_show' - + get 'geolocation_places/new', to: 'geolocation_places#new' post 'geolocation_places/create', to: 'geolocation_places#create' delete 'geolocation_places/:id/delete', to: 'geolocation_places#delete', as: 'geolocation_places_delete' - + get 'geolocation_points/new', to: 'geolocation_points#new' post 'geolocation_points/create', to: 'geolocation_points#create' delete 'geolocation_points/:id/delete', to: 'geolocation_points#delete', as: 'geolocation_points_delete' - + get 'geolocation_boxes/new', to: 'geolocation_boxes#new' post 'geolocation_boxes/create', to: 'geolocation_boxes#create' delete 'geolocation_boxes/:id/delete', to: 'geolocation_boxes#delete', as: 'geolocation_boxes_delete' - + get 'affiliations/autocomplete', to: 'affiliations#autocomplete' get 'affiliations/new', to: 'affiliations#new' post 'affiliations/create', to: 'affiliations#create' delete 'affiliations/:id/delete', to: 'affiliations#delete', as: 'affiliations_delete' - + get 'licenses/details', to: 'licenses#details', as: 'license_details' - + # Actions through Leaflet Ajax posts # points get 'geolocation_points/index', to: 'geolocation_points#index' @@ -462,14 +464,14 @@ # location names/places get 'geolocation_places/places_coordinates', to: 'geolocation_places#places_coordinates' post 'geolocation_places/map_coordinates', to: 'geolocation_places#map_coordinates' - + # get composite views or items that begin at the resource level get 'metadata_entry_pages/find_or_create', to: 'metadata_entry_pages#find_or_create', as: :datacite_metadata_entry_pages get 'metadata_entry_pages/cedar_check', to: 'metadata_entry_pages#cedar_check', as: :metadata_entry_cedar_check get 'resources/review', to: 'resources#review' match 'resources/submission' => 'resources#submission', as: :resources_submission, via: :post get 'resources/show', to: 'resources#show' - + patch 'peer_review/toggle', to: 'peer_review#toggle', as: :peer_review patch 'peer_review/release', to: 'peer_review#release', as: :peer_review_release end @@ -485,9 +487,9 @@ get '/cedar-config', to: 'cedar#json_config' post '/cedar-save', to: 'cedar#save' - + ########################## Dryad v1 support ###################################### - + # Routing to redirect old Dryad URLs to their correct locations in this system get '/pages/faq', to: redirect('stash/requirements') get '/pages/jdap', to: redirect('docs/JointDataArchivingPolicy.pdf') @@ -513,7 +515,7 @@ get '/submit', to: redirect { |params, request| "/stash/resources/new?#{request.params.to_query}" } get '/interested', to: redirect('/stash/contact#get-involved') get '/stash/interested', to: redirect('/stash/contact#get-involved') - + # Routing to redirect old Dryad landing pages to the correct location # Regex based on https://www.crossref.org/blog/dois-and-matching-regular-expressions/ but a little more restrictive specific to old dryad # Dataset: https://datadryad.org/resource/doi:10.5061/dryad.kq201 diff --git a/db/data/20240711144223_populate_last_status_date.rb b/db/data/20240711144223_populate_last_status_date.rb new file mode 100644 index 0000000000..271d29fe4f --- /dev/null +++ b/db/data/20240711144223_populate_last_status_date.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +class PopulateLastStatusDate < ActiveRecord::Migration[7.0] + def up + StashEngine::Resource.find_in_batches(batch_size: 1000) do |batch| + batch.each do |res| + status = res.current_curation_status + date = res.last_curation_activity.created_at + + res.curation_activities.map { |q| [q.status, q.created_at] }.sort { |a, b| b[1] <=> a[1] }.each do |item| + break if status != item[0] + date = item[1] + end + res.process_date.update_column(:last_status_date, date) + end + end + end + + def down + raise ActiveRecord::IrreversibleMigration + end +end diff --git a/db/data_schema.rb b/db/data_schema.rb index 7df91cf713..483ee7c40e 100644 --- a/db/data_schema.rb +++ b/db/data_schema.rb @@ -1 +1 @@ -DataMigrate::Data.define(version: 20240624115611) +DataMigrate::Data.define(version: 20240711144223) diff --git a/db/migrate/20240711144031_add_last_status_date_to_process_dates.rb b/db/migrate/20240711144031_add_last_status_date_to_process_dates.rb new file mode 100644 index 0000000000..50c2eb23a7 --- /dev/null +++ b/db/migrate/20240711144031_add_last_status_date_to_process_dates.rb @@ -0,0 +1,6 @@ +class AddLastStatusDateToProcessDates < ActiveRecord::Migration[7.0] + def change + add_column :stash_engine_process_dates, :last_status_date, :datetime, after: :withdrawn + add_index :stash_engine_process_dates, :last_status_date + end +end diff --git a/lib/tasks/dataset_deletion_tasks.rake b/lib/tasks/dataset_deletion_tasks.rake new file mode 100644 index 0000000000..babd7a009f --- /dev/null +++ b/lib/tasks/dataset_deletion_tasks.rake @@ -0,0 +1,24 @@ +# :nocov: +# rubocop:disable Metrics/BlockLength +namespace :dataset_deletion do + + desc 'Send monthly email reminder to the submitter when a dataset has been `in_progress` for more then 1 month' + task in_progess_reminder: :environment do + p 'Mailing users whose datasets have been in_progress for more then 1 months' + StashEngine::DeleteNotificationsService.new(true).send_in_progress_reminders + end + + desc 'Send monthly email reminder to the submitter when a dataset has been in `action_required` for more then 1 month' + task in_aar_reminder: :environment do + p 'Mailing users whose datasets have been action_required for more then 1 month' + StashEngine::DeleteNotificationsService.new(true).send_action_required_reminders + end + + desc 'Send monthly email reminder to the submitter when a dataset has been in `peer_review` for more then 6 months' + task in_ppr_reminder: :environment do + p 'Mailing users whose datasets have been peer_review for more then 6 months' + StashEngine::DeleteNotificationsService.new(true).send_peer_review_reminders + end +end +# rubocop:enable Metrics/BlockLength +# :nocov: diff --git a/spec/services/stash_engine/delete_notifications_service_spec.rb b/spec/services/stash_engine/delete_notifications_service_spec.rb new file mode 100644 index 0000000000..54e71f5db6 --- /dev/null +++ b/spec/services/stash_engine/delete_notifications_service_spec.rb @@ -0,0 +1,242 @@ +module StashEngine + describe DeleteNotificationsService do + before do + allow_any_instance_of(StashEngine::CurationActivity).to receive(:update_salesforce_metadata).and_return(true) + end + + after :each do + Timecop.return + end + + let!(:user1) { create(:user, email: 'admin@email.test', id: 0) } + let(:user) { create(:user, email: 'some@email.test') } + let(:identifier) { create(:identifier) } + let(:resource) { create(:resource, identifier_id: identifier.id, user_id: user.id) } + + describe '#send_in_progress_reminders' do + let!(:curation_activity) { create(:curation_activity, :in_progress, resource_id: resource.id) } + + before do + allow(StashEngine::ResourceMailer).to receive_message_chain(:in_progress_delete_notification, :deliver_now).and_return(true) + end + + context 'when status date is sooner then one month' do + it 'does not send notification email' do + Timecop.travel(1.day.from_now) + expect(ResourceMailer).to receive(:in_progress_delete_notification).never + expect(subject).to receive(:create_activity).never + + subject.send_in_progress_reminders + end + end + + context 'when status date is older then one year' do + it 'does not send notification email' do + Timecop.travel(13.months.from_now) + expect(ResourceMailer).to receive(:in_progress_delete_notification).never + expect(subject).to receive(:create_activity).never + + subject.send_in_progress_reminders + end + end + + context 'when status date is between 1 month and one year' do + [1, 7, 12].each do |month_num| + it "sends notification email at #{month_num} months" do + Timecop.travel(month_num.months.from_now) + expect(StashEngine::ResourceMailer).to receive_message_chain(:in_progress_delete_notification, :deliver_now).with(resource).with(no_args) + expect(subject).to receive(:create_activity).with('in_progress_deletion_notice', resource).once + + subject.send_in_progress_reminders + end + end + + it 'sends only one email per month' do + resource.last_curation_activity.update!(created_at: 1.months.ago, updated_at: 1.months.ago) + subject.send(:create_activity, 'in_progress_deletion_notice', resource) + + expect(ResourceMailer).to receive(:in_progress_delete_notification).never + + [1.second, 1.day, 2.week, 1.month - 1.day].each do |period| + Timecop.travel(period) do + subject.send_in_progress_reminders + end + end + end + + it 'sends second email after a month' do + resource.last_curation_activity.update!(created_at: 1.months.ago, updated_at: 1.months.ago) + subject.send(:create_activity, 'in_progress_deletion_notice', resource) + + expect(ResourceMailer).to receive(:in_progress_delete_notification).once + Timecop.travel(1.month) do + subject.send_in_progress_reminders + end + end + end + end + + describe '#send_action_required_reminders' do + let!(:curation_activity) { create(:curation_activity, status: 'action_required', resource_id: resource.id) } + + before do + allow(StashEngine::ResourceMailer).to receive_message_chain(:action_required_delete_notification, :deliver_now).and_return(true) + resource.last_curation_activity.update!(status: 'action_required') + end + + context 'when status date is sooner then one month' do + it 'does not send notification email' do + Timecop.travel(1.day.from_now) + expect(ResourceMailer).to receive(:action_required_delete_notification).never + expect(subject).to receive(:create_activity).never + + subject.send_action_required_reminders + end + end + + context 'when status date is older then one year' do + it 'does not send notification email' do + Timecop.travel(13.months.from_now) + expect(ResourceMailer).to receive(:action_required_delete_notification).never + expect(subject).to receive(:create_activity).never + + subject.send_action_required_reminders + end + end + + context 'when status date is between 1 month and one year' do + [1, 7, 12].each do |month_num| + it "sends notification email at #{month_num} months" do + Timecop.travel(month_num.months.from_now) + + expect(StashEngine::ResourceMailer).to receive_message_chain(:action_required_delete_notification, :deliver_now).with(resource).with(no_args) + expect(subject).to receive(:create_activity).with('action_required_deletion_notice', resource).once + + subject.send_action_required_reminders + end + end + + it 'sends only one email per month' do + resource.last_curation_activity.update!(created_at: 2.months.ago) + subject.send(:create_activity, 'action_required_deletion_notice', resource) + + expect(ResourceMailer).to receive(:action_required_delete_notification).never + + [1.second, 1.day, 2.week, 1.month - 1.day].each do |period| + Timecop.travel(period) do + subject.send_action_required_reminders + end + end + end + + it 'sends second email after a month' do + resource.last_curation_activity.update!(created_at: 2.months.ago) + subject.send(:create_activity, 'action_required_deletion_notice', resource) + + expect(ResourceMailer).to receive(:action_required_delete_notification).once + Timecop.travel(2.month + 1.day) do + subject.send_action_required_reminders + end + end + end + end + + describe '#send_peer_review_reminders' do + let!(:curation_activity) { create(:curation_activity, status: 'peer_review', resource_id: resource.id) } + + before do + allow(StashEngine::ResourceMailer).to receive_message_chain(:action_required_delete_notification, :deliver_now).and_return(true) + resource.last_curation_activity.update!(status: 'peer_review') + end + + context 'when status date is sooner then one month' do + before do + resource.last_curation_activity.update!(created_at: 1.day.ago) + end + it 'does not send notification email' do + expect(ResourceMailer).to receive(:peer_review_delete_notification).never + expect(subject).to receive(:create_activity).never + + subject.send_peer_review_reminders + end + end + + context 'when status date is older then one year' do + before do + resource.last_curation_activity.update!(created_at: 13.months.ago) + end + + it 'does not send notification email' do + expect(ResourceMailer).to receive(:peer_review_delete_notification).never + expect(subject).to receive(:create_activity).never + + subject.send_peer_review_reminders + end + end + + context 'when status date is between 6 months and 1 year' do + [6, 7, 9, 12].each do |month_num| + it "sends notification email at #{month_num} months" do + Timecop.travel(month_num.months.from_now) + + expect(StashEngine::ResourceMailer).to receive_message_chain(:peer_review_delete_notification, :deliver_now).with(resource).with(no_args) + expect(subject).to receive(:create_activity).with('peer_review_deletion_notice', resource).once + + subject.send_peer_review_reminders + end + end + + it 'sends only one email per month' do + Timecop.travel(6.months.from_now) do + subject.send(:create_activity, 'peer_review_deletion_notice', resource) + end + expect(ResourceMailer).to receive(:peer_review_delete_notification).never + + [1.second, 1.day, 2.week, 1.month - 1.day].each do |period| + Timecop.travel(6.months.from_now + period) do + subject.send_peer_review_reminders + end + end + end + + it 'sends second email after a month' do + Timecop.travel(6.month.from_now) do + subject.send(:create_activity, 'peer_review_deletion_notice', resource) + end + + expect(ResourceMailer).to receive(:peer_review_delete_notification).once {} + Timecop.travel(7.month.from_now) do + subject.send_peer_review_reminders + end + end + end + end + + describe '#create_activity' do + let(:curation_activity) { create(:curation_activity, status: 'in_progress', resource_id: resource.id) } + it 'creates a new CurationActivity record' do + resource.last_curation_activity.update!(created_at: 1.day.ago) + expect { subject.send(:create_activity, 'flag', resource) }.to change { StashEngine::CurationActivity.count }.by(1) + end + + it 'sets proper data' do + record = subject.send(:create_activity, 'flag', resource) + expect(record.note).to eq('flag - reminded submitter that this item is still `in_progress`') + expect(record.status).to eq('in_progress') + expect(record.resource_id).to eq(resource.id) + expect(record.user_id).to eq(0) + end + end + + describe '#log' do + it 'prints message with logging true' do + expect { subject.class.new(true).send(:log, 'delete_notification_message_with_logs') }.to output(/delete_notification_message_with_logs/).to_stdout + end + + it 'does not print the message with logging false or missing' do + expect { subject.class.new(false).send(:log, 'delete_notification_message_with_logs') }.not_to output(/delete_notification_message_with_logs/).to_stdout + expect { subject.class.new.send(:log, 'delete_notification_message_with_logs') }.not_to output(/delete_notification_message_with_logs/).to_stdout + end + end + end +end From 0533f0065d788fe2f421aa887c744f90299e9c45 Mon Sep 17 00:00:00 2001 From: Alin Vetian Date: Mon, 15 Jul 2024 16:11:34 +0300 Subject: [PATCH 02/14] fixed rubocop offenses --- Gemfile | 2 +- app/mailers/stash_engine/resource_mailer.rb | 5 +---- app/models/stash_engine/curation_activity.rb | 2 +- .../delete_notifications_service.rb | 15 ++++++++++----- lib/tasks/dataset_deletion_tasks.rake | 8 +++----- .../delete_notifications_service_spec.rb | 17 ++++++++++++----- 6 files changed, 28 insertions(+), 21 deletions(-) diff --git a/Gemfile b/Gemfile index c8f35e68a4..4af7ddbab4 100644 --- a/Gemfile +++ b/Gemfile @@ -187,7 +187,7 @@ group :development, :test, :local_dev, :local do # Rails application preloader (https://github.com/rails/spring), says not to install in production gem 'spring' # rspec command for spring (https://github.com/jonleighton/spring-commands-rspec) - gem 'spring-commands-rspec' gem 'letter_opener' gem 'letter_opener_web', '~> 3.0' + gem 'spring-commands-rspec' end diff --git a/app/mailers/stash_engine/resource_mailer.rb b/app/mailers/stash_engine/resource_mailer.rb index f747ad447d..319aaf2311 100644 --- a/app/mailers/stash_engine/resource_mailer.rb +++ b/app/mailers/stash_engine/resource_mailer.rb @@ -12,8 +12,7 @@ def in_progress_delete_notification(resource) mail(to: user_email(@user), subject: "#{rails_env}REMINDER: Dryad Submission \"#{@resource.title}\"") - - end + end def peer_review_delete_notification(resource) logger.warn('Unable to send peer_review_delete_notification; nil resource') unless resource.present? @@ -59,8 +58,6 @@ def assign_variables(resource) @page_error_emails = APP_CONFIG['page_error_email'] || [@helpdesk_email] end - - def rails_env return "[#{Rails.env}] " unless Rails.env.include?('production') diff --git a/app/models/stash_engine/curation_activity.rb b/app/models/stash_engine/curation_activity.rb index 9339e402a8..18276ff814 100644 --- a/app/models/stash_engine/curation_activity.rb +++ b/app/models/stash_engine/curation_activity.rb @@ -227,7 +227,7 @@ def update_solr end def process_dates - update_dates = {last_status_date: Time.current} + update_dates = { last_status_date: Time.current } if first_time_in_status? case status when 'processing', 'peer_review', 'submitted', 'withdrawn' diff --git a/app/services/stash_engine/delete_notifications_service.rb b/app/services/stash_engine/delete_notifications_service.rb index abdedfb811..29a55349b7 100644 --- a/app/services/stash_engine/delete_notifications_service.rb +++ b/app/services/stash_engine/delete_notifications_service.rb @@ -3,7 +3,7 @@ module StashEngine class DeleteNotificationsService attr_reader :logging - def initialize(logging = false) + def initialize(logging: false) @logging = logging end @@ -21,7 +21,7 @@ def send_in_progress_reminders resource.identifier.latest_resource_id == resource.id && (last_reminder.blank? || last_reminder.created_at <= 1.month.ago) - log("Mailing submitter about deletion of in_progress dataset. Identifier: #{resource.identifier_id}, Resource: #{resource.id} updated #{resource.updated_at}") + log_data_for_status('in_progress', resource) StashEngine::ResourceMailer.in_progress_delete_notification(resource).deliver_now create_activity(reminder_flag, resource) end @@ -47,8 +47,7 @@ def send_action_required_reminders resource.identifier.latest_resource_id == resource.id && (last_reminder.blank? || last_reminder.created_at <= 1.month.ago) - log resource.user.email - log("Mailing submitter about deletion of action_required dataset. Identifier: #{resource.identifier_id}, Resource: #{resource.id} updated #{resource.updated_at}") + log_data_for_status('action_required', resource) StashEngine::ResourceMailer.action_required_delete_notification(resource).deliver_now create_activity(reminder_flag, resource) end @@ -70,7 +69,7 @@ def send_peer_review_reminders reminder_flag = 'peer_review_deletion_notice' last_reminder = resource.curation_activities.where('note LIKE ?', "%#{reminder_flag}%")&.last if last_reminder.blank? || last_reminder.created_at <= 1.month.ago - log("Mailing submitter about deletion of peer_review dataset. Identifier: #{resource.identifier_id}, Resource: #{resource.id} updated #{resource.updated_at}") + log_data_for_status('peer_review', resource) StashEngine::ResourceMailer.peer_review_delete_notification(resource).deliver_now create_activity(reminder_flag, resource) end @@ -97,5 +96,11 @@ def log(message) p message end + + def log_data_for_status(status, resource) + text = "Mailing submitter about deletion of #{status} dataset. " + text += " Identifier: #{resource.identifier_id}, Resource: #{resource.id} updated #{resource.updated_at}" + log(text) + end end end diff --git a/lib/tasks/dataset_deletion_tasks.rake b/lib/tasks/dataset_deletion_tasks.rake index babd7a009f..341b2c025a 100644 --- a/lib/tasks/dataset_deletion_tasks.rake +++ b/lib/tasks/dataset_deletion_tasks.rake @@ -1,24 +1,22 @@ # :nocov: -# rubocop:disable Metrics/BlockLength namespace :dataset_deletion do desc 'Send monthly email reminder to the submitter when a dataset has been `in_progress` for more then 1 month' task in_progess_reminder: :environment do p 'Mailing users whose datasets have been in_progress for more then 1 months' - StashEngine::DeleteNotificationsService.new(true).send_in_progress_reminders + StashEngine::DeleteNotificationsService.new(logging: true).send_in_progress_reminders end desc 'Send monthly email reminder to the submitter when a dataset has been in `action_required` for more then 1 month' task in_aar_reminder: :environment do p 'Mailing users whose datasets have been action_required for more then 1 month' - StashEngine::DeleteNotificationsService.new(true).send_action_required_reminders + StashEngine::DeleteNotificationsService.new(logging: true).send_action_required_reminders end desc 'Send monthly email reminder to the submitter when a dataset has been in `peer_review` for more then 6 months' task in_ppr_reminder: :environment do p 'Mailing users whose datasets have been peer_review for more then 6 months' - StashEngine::DeleteNotificationsService.new(true).send_peer_review_reminders + StashEngine::DeleteNotificationsService.new(logging: true).send_peer_review_reminders end end -# rubocop:enable Metrics/BlockLength # :nocov: diff --git a/spec/services/stash_engine/delete_notifications_service_spec.rb b/spec/services/stash_engine/delete_notifications_service_spec.rb index 54e71f5db6..a0541edc0a 100644 --- a/spec/services/stash_engine/delete_notifications_service_spec.rb +++ b/spec/services/stash_engine/delete_notifications_service_spec.rb @@ -109,7 +109,8 @@ module StashEngine it "sends notification email at #{month_num} months" do Timecop.travel(month_num.months.from_now) - expect(StashEngine::ResourceMailer).to receive_message_chain(:action_required_delete_notification, :deliver_now).with(resource).with(no_args) + expect(StashEngine::ResourceMailer).to receive_message_chain(:action_required_delete_notification, + :deliver_now).with(resource).with(no_args) expect(subject).to receive(:create_activity).with('action_required_deletion_notice', resource).once subject.send_action_required_reminders @@ -204,7 +205,7 @@ module StashEngine subject.send(:create_activity, 'peer_review_deletion_notice', resource) end - expect(ResourceMailer).to receive(:peer_review_delete_notification).once {} + expect(ResourceMailer).to receive(:peer_review_delete_notification).once Timecop.travel(7.month.from_now) do subject.send_peer_review_reminders end @@ -230,12 +231,18 @@ module StashEngine describe '#log' do it 'prints message with logging true' do - expect { subject.class.new(true).send(:log, 'delete_notification_message_with_logs') }.to output(/delete_notification_message_with_logs/).to_stdout + expect do + subject.class.new(logging: true).send(:log, 'delete_notification_message_with_logs') + end.to output(/delete_notification_message_with_logs/).to_stdout end it 'does not print the message with logging false or missing' do - expect { subject.class.new(false).send(:log, 'delete_notification_message_with_logs') }.not_to output(/delete_notification_message_with_logs/).to_stdout - expect { subject.class.new.send(:log, 'delete_notification_message_with_logs') }.not_to output(/delete_notification_message_with_logs/).to_stdout + expect do + subject.class.new(logging: false).send(:log, 'delete_notification_message_with_logs') + end.not_to output(/delete_notification_message_with_logs/).to_stdout + expect do + subject.class.new.send(:log, 'delete_notification_message_with_logs') + end.not_to output(/delete_notification_message_with_logs/).to_stdout end end end From da8f694e4561c4ed4ac45a464c072b8ff020ecb5 Mon Sep 17 00:00:00 2001 From: Alin Vetian Date: Wed, 17 Jul 2024 18:52:18 +0300 Subject: [PATCH 03/14] added withdrawn functionality and email notification, added final delete notification --- app/mailers/stash_engine/resource_mailer.rb | 22 ++ .../delete_notifications_service.rb | 61 ++++- ...send_final_withdrawn_notification.html.erb | 19 ++ ...end_set_to_withdrawn_notification.html.erb | 18 ++ .../delete_notifications_service_spec.rb | 228 +++++++++++++++++- 5 files changed, 332 insertions(+), 16 deletions(-) create mode 100644 app/views/stash_engine/resource_mailer/send_final_withdrawn_notification.html.erb create mode 100644 app/views/stash_engine/resource_mailer/send_set_to_withdrawn_notification.html.erb diff --git a/app/mailers/stash_engine/resource_mailer.rb b/app/mailers/stash_engine/resource_mailer.rb index 319aaf2311..0be98b9df0 100644 --- a/app/mailers/stash_engine/resource_mailer.rb +++ b/app/mailers/stash_engine/resource_mailer.rb @@ -36,6 +36,28 @@ def action_required_delete_notification(resource) subject: "#{rails_env}REMINDER: Dryad Submission \"#{@resource.title}\"") end + def send_set_to_withdrawn_notification(resource) + logger.warn('Unable to send set_to_withdrawn_notification; nil resource') unless resource.present? + return unless resource.present? + + assign_variables(resource) + return unless @user.present? && user_email(@user).present? + + mail(to: user_email(@user), + subject: "#{rails_env}NOTIFICATION: Dryad resource set to withdrawn \"#{@resource.title}\"") + end + + def send_final_withdrawn_notification + logger.warn('Unable to send send_final_withdrawn_notification; nil resource') unless resource.present? + return unless resource.present? + + assign_variables(resource) + return unless @user.present? && user_email(@user).present? + + mail(to: user_email(@user), + subject: "#{rails_env}FINAL NOTIFICATION: Dryad resource will be deleted \"#{@resource.title}\"") + end + private # rubocop:disable Style/NestedTernaryOperator diff --git a/app/services/stash_engine/delete_notifications_service.rb b/app/services/stash_engine/delete_notifications_service.rb index 29a55349b7..87e2eababb 100644 --- a/app/services/stash_engine/delete_notifications_service.rb +++ b/app/services/stash_engine/delete_notifications_service.rb @@ -79,15 +79,64 @@ def send_peer_review_reminders true end + # Send withdrawn email notification + # - email is sent once at 1 year + # - the resource is set to Withdrawn status + def send_withdrawn_notification + StashEngine::Resource.latest_per_dataset.joins(:last_curation_activity).joins(:process_date) + .where(stash_engine_curation_activities: { status: %w[peer_review action_required] }) + .where(stash_engine_process_dates: { last_status_date: 1.year.ago.beginning_of_day..1.year.ago.end_of_day }) + .each do |resource| + + reminder_flag = 'withdrawn_email_notice' + last_reminder = resource.curation_activities.where('note LIKE ?', "%#{reminder_flag}%")&.last + next if last_reminder.present? + + status_updated = create_activity(reminder_flag, resource, status: 'withdrawn', + note: "#{reminder_flag} - notification that this item was set to `withdrawn`") + + if status_updated + log("Mailing submitter about setting dataset to withdrawn. #{resource_log_text(resource)}") + StashEngine::ResourceMailer.send_set_to_withdrawn_notification(resource).deliver_now + end + rescue StandardError => e + p " Exception! #{e.message}" + end + true + end + + # Send final withdrawn email notification + # - email is sent once, 9 months after the resource was withdraw + # - the email is sent only if resource was never published + def send_final_withdrawn_notification + StashEngine::Resource.latest_per_dataset.joins(:last_curation_activity).joins(:process_date) + .where(stash_engine_curation_activities: { status: 'withdrawn' }) + .where('stash_engine_process_dates.last_status_date <= ?', 9.months.ago.end_of_day) + .each do |resource| + next if resource.curation_activities.pluck(:status).uniq.include?('published') + + reminder_flag = 'final_withdrawn_email_notice' + last_reminder = resource.curation_activities.where('note LIKE ?', "%#{reminder_flag}%")&.last + next if last_reminder.present? + + log("Mailing submitter as final withdrawn notification. #{resource_log_text(resource)}") + StashEngine::ResourceMailer.send_final_withdrawn_notification(resource).deliver_now + create_activity(reminder_flag, resource) + rescue StandardError => e + p " Exception! #{e.message}" + end + true + end + private - def create_activity(flag, resource) - status = resource.last_curation_activity.status + def create_activity(flag, resource, status: nil, note: nil) + status ||= resource.last_curation_activity.status StashEngine::CurationActivity.create( resource_id: resource.id, user_id: 0, status: status, - note: "#{flag} - reminded submitter that this item is still `#{status}`" + note: note || "#{flag} - reminded submitter that this item is still `#{status}`" ) end @@ -99,8 +148,12 @@ def log(message) def log_data_for_status(status, resource) text = "Mailing submitter about deletion of #{status} dataset. " - text += " Identifier: #{resource.identifier_id}, Resource: #{resource.id} updated #{resource.updated_at}" + text += resource_log_text(resource) log(text) end + + def resource_log_text(resource) + " Identifier: #{resource.identifier_id}, Resource: #{resource.id} updated #{resource.updated_at}" + end end end diff --git a/app/views/stash_engine/resource_mailer/send_final_withdrawn_notification.html.erb b/app/views/stash_engine/resource_mailer/send_final_withdrawn_notification.html.erb new file mode 100644 index 0000000000..8b14831488 --- /dev/null +++ b/app/views/stash_engine/resource_mailer/send_final_withdrawn_notification.html.erb @@ -0,0 +1,19 @@ +

Dear <%= @user_name %>,

+ +

+ This is the final notice before deleting your dataset. + Your dataset will be deleted in 3 months: +

+ +

+ Title: "<%= @resource.title %>"
+ DOI: <%= @resource.identifier_str %> +

+ +

+ Our previous email correspondence contains specific details on the changes required. Please contact us at help@datadryad.org if you have any questions or no longer wish to proceed with the publication of the dataset. +

+ +

We hope to hear from you shortly.

+ +

The Dryad Team

diff --git a/app/views/stash_engine/resource_mailer/send_set_to_withdrawn_notification.html.erb b/app/views/stash_engine/resource_mailer/send_set_to_withdrawn_notification.html.erb new file mode 100644 index 0000000000..88f269df1b --- /dev/null +++ b/app/views/stash_engine/resource_mailer/send_set_to_withdrawn_notification.html.erb @@ -0,0 +1,18 @@ +

Dear <%= @user_name %>,

+ +

+ Your dataset was withdrawn due to inactivity: +

+ +

+ Title: "<%= @resource.title %>"
+ DOI: <%= @resource.identifier_str %> +

+ +

+ Our previous email correspondence contains specific details on the changes required. Please contact us at help@datadryad.org if you have any questions or no longer wish to proceed with the publication of the dataset. +

+ +

We hope to hear from you shortly.

+ +

The Dryad Team

diff --git a/spec/services/stash_engine/delete_notifications_service_spec.rb b/spec/services/stash_engine/delete_notifications_service_spec.rb index a0541edc0a..31d3f19d3a 100644 --- a/spec/services/stash_engine/delete_notifications_service_spec.rb +++ b/spec/services/stash_engine/delete_notifications_service_spec.rb @@ -23,7 +23,7 @@ module StashEngine context 'when status date is sooner then one month' do it 'does not send notification email' do Timecop.travel(1.day.from_now) - expect(ResourceMailer).to receive(:in_progress_delete_notification).never + expect(StashEngine::ResourceMailer).to receive(:in_progress_delete_notification).never expect(subject).to receive(:create_activity).never subject.send_in_progress_reminders @@ -33,7 +33,7 @@ module StashEngine context 'when status date is older then one year' do it 'does not send notification email' do Timecop.travel(13.months.from_now) - expect(ResourceMailer).to receive(:in_progress_delete_notification).never + expect(StashEngine::ResourceMailer).to receive(:in_progress_delete_notification).never expect(subject).to receive(:create_activity).never subject.send_in_progress_reminders @@ -55,7 +55,7 @@ module StashEngine resource.last_curation_activity.update!(created_at: 1.months.ago, updated_at: 1.months.ago) subject.send(:create_activity, 'in_progress_deletion_notice', resource) - expect(ResourceMailer).to receive(:in_progress_delete_notification).never + expect(StashEngine::ResourceMailer).to receive(:in_progress_delete_notification).never [1.second, 1.day, 2.week, 1.month - 1.day].each do |period| Timecop.travel(period) do @@ -68,7 +68,7 @@ module StashEngine resource.last_curation_activity.update!(created_at: 1.months.ago, updated_at: 1.months.ago) subject.send(:create_activity, 'in_progress_deletion_notice', resource) - expect(ResourceMailer).to receive(:in_progress_delete_notification).once + expect(StashEngine::ResourceMailer).to receive(:in_progress_delete_notification).once Timecop.travel(1.month) do subject.send_in_progress_reminders end @@ -87,7 +87,7 @@ module StashEngine context 'when status date is sooner then one month' do it 'does not send notification email' do Timecop.travel(1.day.from_now) - expect(ResourceMailer).to receive(:action_required_delete_notification).never + expect(StashEngine::ResourceMailer).to receive(:action_required_delete_notification).never expect(subject).to receive(:create_activity).never subject.send_action_required_reminders @@ -97,7 +97,7 @@ module StashEngine context 'when status date is older then one year' do it 'does not send notification email' do Timecop.travel(13.months.from_now) - expect(ResourceMailer).to receive(:action_required_delete_notification).never + expect(StashEngine::ResourceMailer).to receive(:action_required_delete_notification).never expect(subject).to receive(:create_activity).never subject.send_action_required_reminders @@ -121,7 +121,7 @@ module StashEngine resource.last_curation_activity.update!(created_at: 2.months.ago) subject.send(:create_activity, 'action_required_deletion_notice', resource) - expect(ResourceMailer).to receive(:action_required_delete_notification).never + expect(StashEngine::ResourceMailer).to receive(:action_required_delete_notification).never [1.second, 1.day, 2.week, 1.month - 1.day].each do |period| Timecop.travel(period) do @@ -134,7 +134,7 @@ module StashEngine resource.last_curation_activity.update!(created_at: 2.months.ago) subject.send(:create_activity, 'action_required_deletion_notice', resource) - expect(ResourceMailer).to receive(:action_required_delete_notification).once + expect(StashEngine::ResourceMailer).to receive(:action_required_delete_notification).once Timecop.travel(2.month + 1.day) do subject.send_action_required_reminders end @@ -155,7 +155,7 @@ module StashEngine resource.last_curation_activity.update!(created_at: 1.day.ago) end it 'does not send notification email' do - expect(ResourceMailer).to receive(:peer_review_delete_notification).never + expect(StashEngine::ResourceMailer).to receive(:peer_review_delete_notification).never expect(subject).to receive(:create_activity).never subject.send_peer_review_reminders @@ -168,7 +168,7 @@ module StashEngine end it 'does not send notification email' do - expect(ResourceMailer).to receive(:peer_review_delete_notification).never + expect(StashEngine::ResourceMailer).to receive(:peer_review_delete_notification).never expect(subject).to receive(:create_activity).never subject.send_peer_review_reminders @@ -191,7 +191,7 @@ module StashEngine Timecop.travel(6.months.from_now) do subject.send(:create_activity, 'peer_review_deletion_notice', resource) end - expect(ResourceMailer).to receive(:peer_review_delete_notification).never + expect(StashEngine::ResourceMailer).to receive(:peer_review_delete_notification).never [1.second, 1.day, 2.week, 1.month - 1.day].each do |period| Timecop.travel(6.months.from_now + period) do @@ -205,7 +205,7 @@ module StashEngine subject.send(:create_activity, 'peer_review_deletion_notice', resource) end - expect(ResourceMailer).to receive(:peer_review_delete_notification).once + expect(StashEngine::ResourceMailer).to receive(:peer_review_delete_notification).once Timecop.travel(7.month.from_now) do subject.send_peer_review_reminders end @@ -213,6 +213,210 @@ module StashEngine end end + describe '#send_withdrawn_notification' do + let!(:curation_activity) { create(:curation_activity, status: 'peer_review', resource_id: resource.id) } + + before do + allow(StashEngine::ResourceMailer).to receive_message_chain(:send_set_to_withdrawn_notification, :deliver_now).and_return(true) + resource.last_curation_activity.update!(status: 'peer_review') + end + + context 'when status date is sooner then one year' do + it 'does not send notification email' do + Timecop.travel(11.months.from_now) + expect(StashEngine::ResourceMailer).to receive(:send_set_to_withdrawn_notification).never + expect(subject).to receive(:create_activity).never + + subject.send_withdrawn_notification + end + end + + context 'when status date is older then 1 year' do + it "creates withdrawn activity notification at 1 year" do + Timecop.travel(1.year.from_now) + + expect(subject).to receive(:create_activity).with('withdrawn_email_notice', resource, { + note: "withdrawn_email_notice - notification that this item was set to `withdrawn`", + status: "withdrawn" + }).once + + subject.send_withdrawn_notification + end + + it "sends notification email at 1 year" do + Timecop.travel(1.year.from_now) + + expect(StashEngine::ResourceMailer).to receive_message_chain(:send_set_to_withdrawn_notification, :deliver_now).with(resource).with(no_args) + + subject.send_withdrawn_notification + end + + it 'sends only one email ever' do + Timecop.travel(1.year.from_now) do + subject.send(:create_activity, 'withdrawn_email_notice', resource) + end + expect(StashEngine::ResourceMailer).to receive(:send_set_to_withdrawn_notification).never + + [1.second, 1.day, 2.week, 1.month, 10.years].each do |period| + Timecop.travel(1.year.from_now + period) do + subject.send_withdrawn_notification + end + end + end + end + end + + describe '#send_withdrawn_notification' do + context 'for in peer_review resource' do + let!(:curation_activity) { create(:curation_activity, status: 'peer_review', resource_id: resource.id) } + + before do + allow(StashEngine::ResourceMailer).to receive_message_chain(:send_set_to_withdrawn_notification, :deliver_now).and_return(true) + resource.last_curation_activity.update!(status: 'peer_review') + end + + context 'when status date is sooner then one year' do + it 'does not send notification email' do + Timecop.travel(11.months.from_now) + expect(StashEngine::ResourceMailer).to receive(:send_set_to_withdrawn_notification).never + expect(subject).to receive(:create_activity).never + + subject.send_withdrawn_notification + end + end + + context 'when status date is older then 1 year' do + it "creates withdrawn activity notification at 1 year" do + Timecop.travel(1.year.from_now) + + expect(subject).to receive(:create_activity).with('withdrawn_email_notice', resource, { + note: "withdrawn_email_notice - notification that this item was set to `withdrawn`", + status: "withdrawn" + }).once + + subject.send_withdrawn_notification + end + + it "sends notification email at 1 year" do + Timecop.travel(1.year.from_now) + + expect(StashEngine::ResourceMailer).to receive_message_chain(:send_set_to_withdrawn_notification, :deliver_now).with(resource).with(no_args) + + subject.send_withdrawn_notification + end + + it 'sends only one email ever' do + Timecop.travel(1.year.from_now) do + subject.send(:create_activity, 'withdrawn_email_notice', resource) + end + expect(StashEngine::ResourceMailer).to receive(:send_set_to_withdrawn_notification).never + + [1.second, 1.day, 2.week, 1.month, 10.years].each do |period| + Timecop.travel(1.year.from_now + period) do + subject.send_withdrawn_notification + end + end + end + end + end + + context 'for in action_required resource' do + let!(:curation_activity) { create(:curation_activity, status: 'action_required', resource_id: resource.id) } + + before do + allow(StashEngine::ResourceMailer).to receive_message_chain(:send_set_to_withdrawn_notification, :deliver_now).and_return(true) + resource.last_curation_activity.update!(status: 'action_required') + end + + context 'when status date is sooner then one year' do + it 'does not send notification email' do + Timecop.travel(11.months.from_now) + expect(StashEngine::ResourceMailer).to receive(:send_set_to_withdrawn_notification).never + expect(subject).to receive(:create_activity).never + + subject.send_withdrawn_notification + end + end + + context 'when status date is older then 1 year' do + it "creates withdrawn activity notification at 1 year" do + Timecop.travel(1.year.from_now) + + expect(subject).to receive(:create_activity).with('withdrawn_email_notice', resource, { + note: "withdrawn_email_notice - notification that this item was set to `withdrawn`", + status: "withdrawn" + }).once + + subject.send_withdrawn_notification + end + + it "sends notification email at 1 year" do + Timecop.travel(1.year.from_now) + + expect(StashEngine::ResourceMailer).to receive_message_chain(:send_set_to_withdrawn_notification, :deliver_now).with(resource).with(no_args) + + subject.send_withdrawn_notification + end + + it 'sends only one email ever' do + Timecop.travel(1.year.from_now) do + subject.send(:create_activity, 'withdrawn_email_notice', resource) + end + expect(StashEngine::ResourceMailer).to receive(:send_set_to_withdrawn_notification).never + + [1.second, 1.day, 2.week, 1.month, 10.years].each do |period| + Timecop.travel(1.year.from_now + period) do + subject.send_withdrawn_notification + end + end + end + end + end + end + + describe '#send_final_withdrawn_notification' do + let!(:curation_activity) { create(:curation_activity, status: 'withdrawn', resource_id: resource.id) } + + before do + allow(StashEngine::ResourceMailer).to receive_message_chain(:send_final_withdrawn_notification, :deliver_now).and_return(true) + resource.last_curation_activity.update!(status: 'withdrawn') + end + + context 'when status date is sooner then 9 months' do + it 'does not send notification email' do + Timecop.travel(9.months.from_now-1.day) + expect(StashEngine::ResourceMailer).to receive(:send_final_withdrawn_notification).never + expect(subject).to receive(:create_activity).never + + subject.send_final_withdrawn_notification + end + end + + context 'when status date is older then 9 months' do + it "sends notification email at 9 months" do + Timecop.travel(9.months.from_now) + + expect(StashEngine::ResourceMailer).to receive_message_chain(:send_final_withdrawn_notification, :deliver_now).with(resource).with(no_args) + expect(subject).to receive(:create_activity).with('final_withdrawn_email_notice', resource).once + + subject.send_final_withdrawn_notification + end + + it 'sends only one email ever' do + Timecop.travel(9.months.from_now) do + subject.send(:create_activity, 'final_withdrawn_email_notice', resource) + end + expect(StashEngine::ResourceMailer).to receive(:send_final_withdrawn_notification).never + + [1.second, 1.day, 2.week, 1.month, 10.years].each do |period| + Timecop.travel(9.months.from_now + period) do + subject.send_final_withdrawn_notification + end + end + end + end + end + describe '#create_activity' do let(:curation_activity) { create(:curation_activity, status: 'in_progress', resource_id: resource.id) } it 'creates a new CurationActivity record' do From acbf2702efb39c665d96b4ae1e9e43a0ec51e913 Mon Sep 17 00:00:00 2001 From: Alin Vetian Date: Fri, 19 Jul 2024 09:49:11 +0300 Subject: [PATCH 04/14] added option for admin users to change deletion date reference --- .../admin_dashboard_controller.rb | 28 ++++++++++++- app/models/stash_engine/curation_activity.rb | 19 +++++++-- .../delete_notifications_service.rb | 12 +++--- ...tifier_delete_reference_date_form.html.erb | 30 ++++++++++++++ .../edit_delete_reference_date.js.erb | 8 ++++ .../update_delete_reference_date.js.erb | 11 +++++ .../_activity_log_notifications.html.erb | 14 +++++++ .../admin_datasets/activity_log.html.erb | 4 +- config/routes.rb | 2 + ...0240711144223_populate_last_status_date.rb | 3 +- ...te_calculation_date_to_processing_dates.rb | 6 +++ .../delete_notifications_service_spec.rb | 40 ++++++++++--------- 12 files changed, 145 insertions(+), 32 deletions(-) create mode 100644 app/views/stash_engine/admin_dashboard/_identifier_delete_reference_date_form.html.erb create mode 100644 app/views/stash_engine/admin_dashboard/edit_delete_reference_date.js.erb create mode 100644 app/views/stash_engine/admin_dashboard/update_delete_reference_date.js.erb create mode 100644 app/views/stash_engine/admin_datasets/_activity_log_notifications.html.erb create mode 100644 db/migrate/20240718050818_add_delete_calculation_date_to_processing_dates.rb diff --git a/app/controllers/stash_engine/admin_dashboard_controller.rb b/app/controllers/stash_engine/admin_dashboard_controller.rb index 7030c3b3cf..42c4c388a2 100644 --- a/app/controllers/stash_engine/admin_dashboard_controller.rb +++ b/app/controllers/stash_engine/admin_dashboard_controller.rb @@ -7,7 +7,7 @@ class AdminDashboardController < ApplicationController before_action :setup_paging, only: %i[results] before_action :setup_limits, only: %i[index results] before_action :setup_search, only: %i[index results] - before_action :load, only: %i[edit update] + before_action :load, only: %i[edit update edit_delete_reference_date update_delete_reference_date] def index; end @@ -80,6 +80,25 @@ def update respond_to(&:js) end + def edit_delete_reference_date + @desc = 'Edit dataset deletion date reference' + @process_date = @identifier.process_date + respond_to(&:js) + end + + def update_delete_reference_date + delete_calculation_date = params.dig(:process_date, :delete_calculation_date) + return error_response('Date can not be blank') if delete_calculation_date.blank? + + params[:curation_activity][:note] = "Changed deletion reference date to #{delete_calculation_date}. #{params[:curation_activity][:note]}".html_safe + curation_activity_change + + @identifier.process_date.update(delete_calculation_date: delete_calculation_date) + @resource.process_date.update(delete_calculation_date: delete_calculation_date) + @curation_activity = @resource.last_curation_activity + respond_to(&:js) + end + private def collect_properties @@ -406,5 +425,12 @@ def current_editor_change end end + + def error_response(message) + @error_message = <<-HTML.chomp.html_safe + #{message} + HTML + render :curation_activity_error + end # rubocop:enable Metrics/ClassLength end diff --git a/app/models/stash_engine/curation_activity.rb b/app/models/stash_engine/curation_activity.rb index 18276ff814..66ba6b08c0 100644 --- a/app/models/stash_engine/curation_activity.rb +++ b/app/models/stash_engine/curation_activity.rb @@ -113,9 +113,9 @@ class CurationActivity < ApplicationRecord # rubocop:disable Metrics/ClassLength if: proc { |ca| ca.published? && curation_status_changed? && !resource.skip_emails } after_create :update_salesforce_metadata, if: proc { |_ca| - curation_status_changed? && - CurationActivity.where(resource_id: resource_id).count > 1 - } + curation_status_changed? && + CurationActivity.where(resource_id: resource_id).count > 1 + } # Class methods # ------------------------------------------ @@ -187,6 +187,7 @@ def self.allowed_states(current_state) # Private methods # ------------------------------------------ + private # Callbacks @@ -227,7 +228,10 @@ def update_solr end def process_dates - update_dates = { last_status_date: Time.current } + update_dates = { last_status_date: created_at } + # update delete_calculation_date if the status changed after the date set by the curators + update_dates[:delete_calculation_date] = delete_calculation_date_value + if first_time_in_status? case status when 'processing', 'peer_review', 'submitted', 'withdrawn' @@ -407,5 +411,12 @@ def user_name 'System' end + + def delete_calculation_date_value + existing_date = resource.identifier.process_date[:delete_calculation_date] + return created_at if existing_date.blank? + + [created_at, existing_date].max + end end end diff --git a/app/services/stash_engine/delete_notifications_service.rb b/app/services/stash_engine/delete_notifications_service.rb index 87e2eababb..66f4f7f254 100644 --- a/app/services/stash_engine/delete_notifications_service.rb +++ b/app/services/stash_engine/delete_notifications_service.rb @@ -13,7 +13,7 @@ def initialize(logging: false) def send_in_progress_reminders StashEngine::Resource.latest_per_dataset.joins(:last_curation_activity).joins(:process_date) .where(stash_engine_curation_activities: { status: 'in_progress' }) - .where(stash_engine_process_dates: { last_status_date: 1.year.ago.beginning_of_day..1.months.ago.end_of_day }) + .where(stash_engine_process_dates: { delete_calculation_date: 1.year.ago.beginning_of_day..1.months.ago.end_of_day }) .each do |resource| reminder_flag = 'in_progress_deletion_notice' last_reminder = resource.curation_activities.where('note LIKE ?', "%#{reminder_flag}%")&.last @@ -37,7 +37,7 @@ def send_in_progress_reminders def send_action_required_reminders StashEngine::Resource.latest_per_dataset.joins(:last_curation_activity).joins(:process_date) .where(stash_engine_curation_activities: { status: 'action_required' }) - .where(stash_engine_process_dates: { last_status_date: 1.year.ago.beginning_of_day..1.months.ago.end_of_day }) + .where(stash_engine_process_dates: { delete_calculation_date: 1.year.ago.beginning_of_day..1.months.ago.end_of_day }) .each do |resource| reminder_flag = 'action_required_deletion_notice' @@ -63,7 +63,7 @@ def send_action_required_reminders def send_peer_review_reminders StashEngine::Resource.latest_per_dataset.joins(:last_curation_activity).joins(:process_date) .where(stash_engine_curation_activities: { status: 'peer_review' }) - .where(stash_engine_process_dates: { last_status_date: 1.year.ago.beginning_of_day..6.months.ago.end_of_day }) + .where(stash_engine_process_dates: { delete_calculation_date: 1.year.ago.beginning_of_day..6.months.ago.end_of_day }) .each do |resource| reminder_flag = 'peer_review_deletion_notice' @@ -85,7 +85,7 @@ def send_peer_review_reminders def send_withdrawn_notification StashEngine::Resource.latest_per_dataset.joins(:last_curation_activity).joins(:process_date) .where(stash_engine_curation_activities: { status: %w[peer_review action_required] }) - .where(stash_engine_process_dates: { last_status_date: 1.year.ago.beginning_of_day..1.year.ago.end_of_day }) + .where(stash_engine_process_dates: { delete_calculation_date: 1.year.ago.beginning_of_day..1.year.ago.end_of_day }) .each do |resource| reminder_flag = 'withdrawn_email_notice' @@ -93,7 +93,7 @@ def send_withdrawn_notification next if last_reminder.present? status_updated = create_activity(reminder_flag, resource, status: 'withdrawn', - note: "#{reminder_flag} - notification that this item was set to `withdrawn`") + note: "#{reminder_flag} - notification that this item was set to `withdrawn`") if status_updated log("Mailing submitter about setting dataset to withdrawn. #{resource_log_text(resource)}") @@ -111,7 +111,7 @@ def send_withdrawn_notification def send_final_withdrawn_notification StashEngine::Resource.latest_per_dataset.joins(:last_curation_activity).joins(:process_date) .where(stash_engine_curation_activities: { status: 'withdrawn' }) - .where('stash_engine_process_dates.last_status_date <= ?', 9.months.ago.end_of_day) + .where('stash_engine_process_dates.delete_calculation_date <= ?', 9.months.ago.end_of_day) .each do |resource| next if resource.curation_activities.pluck(:status).uniq.include?('published') diff --git a/app/views/stash_engine/admin_dashboard/_identifier_delete_reference_date_form.html.erb b/app/views/stash_engine/admin_dashboard/_identifier_delete_reference_date_form.html.erb new file mode 100644 index 0000000000..3825b2c9d1 --- /dev/null +++ b/app/views/stash_engine/admin_dashboard/_identifier_delete_reference_date_form.html.erb @@ -0,0 +1,30 @@ +

<%= @desc %>

+<%= form_with(url: admin_dash_update_delete_reference_date_path(@identifier.id), method: :post, local: false) do |form| -%> + <%= hidden_field_tag :field, @field %> + +

Edit deletion date reference for <%= @resource.title %>

+

Current reference date is <%= @resource.identifier.process_date.delete_calculation_date %>

+ <%= hidden_field_tag :identifier_id, @resource.identifier_id %> + <%# Users cannot change the status or publication date once the files are published %> + <%# if @resource.curatable? && filter_status_select(@resource.current_curation_status) %> + <%= form.fields_for :process_date, @process_date do |ca| %> +
+ <%= ca.label :delete_calculation_date, 'Date', class: 'c-input__label' %> + <%= ca.date_field :delete_calculation_date, class: 'c-input__text' %> +
+ <% end %> + <%# else %> + + <%# end %> + + <%= form.fields_for :curation_activity, @curation_activity do |ca| %> +
+ <%= ca.label :note, 'Notes', class: 'c-input__label' %> + <%= ca.text_area :note, class: 'c-input__textarea', id: 'activity_note', style: 'width: 100%;' %> +
+ <% end %> +
+ <%= submit_tag 'Submit', class: 'o-button__plain-text2', id: 'popup_submit' %> + <%= button_tag 'Cancel', type: 'button', id: 'cancel_dialog', class: 'o-button__plain-text7' %> +
+<% end %> diff --git a/app/views/stash_engine/admin_dashboard/edit_delete_reference_date.js.erb b/app/views/stash_engine/admin_dashboard/edit_delete_reference_date.js.erb new file mode 100644 index 0000000000..93808b5bea --- /dev/null +++ b/app/views/stash_engine/admin_dashboard/edit_delete_reference_date.js.erb @@ -0,0 +1,8 @@ +// open a modal dialog +document.getElementById('genericModalContent').innerHTML = "<%= escape_javascript(render partial: 'identifier_delete_reference_date_form') %>"; +document.getElementById('genericModalDialog').showModal(); +document.getElementById('cancel_dialog').addEventListener('click', (e) => { + e.preventDefault(); + document.getElementById('genericModalDialog').close(); +}); +initDatePicker(); diff --git a/app/views/stash_engine/admin_dashboard/update_delete_reference_date.js.erb b/app/views/stash_engine/admin_dashboard/update_delete_reference_date.js.erb new file mode 100644 index 0000000000..2852fb674a --- /dev/null +++ b/app/views/stash_engine/admin_dashboard/update_delete_reference_date.js.erb @@ -0,0 +1,11 @@ +document.getElementById('genericModalDialog').close(); + +if($("#curation_table .c-lined-table__sort-asc").length > 0){ + $('#curation_table tr:last').after("<%= escape_javascript( + render partial: 'stash_engine/admin_datasets/activity_log_row', locals: { curation_activity: @curation_activity }) + %>"); +}else{ + $('#curation_table thead').after("<%= escape_javascript( + render partial: 'stash_engine/admin_datasets/activity_log_row', locals: { curation_activity: @curation_activity }) + %>"); +} diff --git a/app/views/stash_engine/admin_datasets/_activity_log_notifications.html.erb b/app/views/stash_engine/admin_datasets/_activity_log_notifications.html.erb new file mode 100644 index 0000000000..2253ba8bd9 --- /dev/null +++ b/app/views/stash_engine/admin_datasets/_activity_log_notifications.html.erb @@ -0,0 +1,14 @@ +
+

Notifications

+ + <% if policy([:stash_engine, :admin_datasets]).data_popup? %> +
+
+ <%= form_with(url: admin_dash_edit_delete_reference_date_path(id: @identifier&.id, field: 'curation_activity'), method: :get, local: false) do %> + + <% end %> +
+ <% end %> +
diff --git a/app/views/stash_engine/admin_datasets/activity_log.html.erb b/app/views/stash_engine/admin_datasets/activity_log.html.erb index 4005076f04..01e820e881 100644 --- a/app/views/stash_engine/admin_datasets/activity_log.html.erb +++ b/app/views/stash_engine/admin_datasets/activity_log.html.erb @@ -43,6 +43,8 @@ +<%= render partial: 'stash_engine/admin_datasets/activity_log_notifications' %> + <% if policy([:stash_engine, :admin_datasets]).create_salesforce_case? && Stash::Salesforce.sf_user %>