Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Need to Include dry/monads in sample app to get generators working #916

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions app/assets/javascripts/bulkrax/datatables.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
Blacklight.onLoad(function() {
if($('#importer-show-table').length) {
$('#importer-show-table').DataTable( {
'processing': true,
'serverSide': true,
"ajax": window.location.href.replace(/(\/importers\/\d+)/, "$1/entry_table.json"),
"pageLength": 30,
"lengthMenu": [[30, 100, 200], [30, 100, 200]],
"columns": [
{ "data": "identifier" },
{ "data": "id" },
{ "data": "status_message" },
{ "data": "type" },
{ "data": "updated_at" },
{ "data": "errors", "orderable": false },
{ "data": "actions", "orderable": false }
],
"dom": '<"toolbar">frtip',
initComplete: function () {
// Add entry class filter
let entrySelect = document.createElement('select')
entrySelect.id = 'entry-filter'
entrySelect.classList.value = 'form-control input-sm'
entrySelect.style.marginRight = '10px'

entrySelect.add(new Option('Filter by Entry Class', ''))
// Read the options from the footer and add them to the entrySelect
$('#importer-entry-classes').text().split('|').forEach(function (col, i) {
entrySelect.add(new Option(col.trim()))
})
document.querySelector('div#importer-show-table_filter').firstChild.prepend(entrySelect)

// Apply listener for user change in value
entrySelect.addEventListener('change', function () {
var val = entrySelect.value;
this.api()
.search(val ? val : '', false, false)
.draw();
}.bind(this));

// Add status filter
let statusSelect = document.createElement('select');
statusSelect.id = 'status-filter'
statusSelect.classList.value = 'form-control input-sm'
statusSelect.style.marginRight = '10px'

statusSelect.add(new Option('Filter by Status', ''));
statusSelect.add(new Option('Complete'))
statusSelect.add(new Option('Pending'))
statusSelect.add(new Option('Failed'))
document.querySelector('div#importer-show-table_filter').firstChild.prepend(statusSelect)

// Apply listener for user change in value
statusSelect.addEventListener('change', function () {
var val = statusSelect.value;
this.api()
.search(val ? val : '', false, false)
.draw();
}.bind(this));

// Add refresh link
let refreshLink = document.createElement('a');
refreshLink.onclick = function() {
this.api().ajax.reload(null, false)
}.bind(this)
refreshLink.classList.value = 'glyphicon glyphicon-refresh'
refreshLink.style.marginLeft = '10px'
document.querySelector('div#importer-show-table_filter').firstChild.append(refreshLink)
}
} );
}
})
17 changes: 12 additions & 5 deletions app/controllers/bulkrax/importers_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ class ImportersController < ::Bulkrax::ApplicationController
include Hyrax::ThemedLayoutController if defined?(::Hyrax)
include Bulkrax::DownloadBehavior
include Bulkrax::API
include Bulkrax::DatatablesBehavior
include Bulkrax::ValidationHelper

protect_from_forgery unless: -> { api_request? }
before_action :token_authenticate!, if: -> { api_request? }, only: [:create, :update, :delete]
before_action :authenticate_user!, unless: -> { api_request? }
before_action :check_permissions
before_action :set_importer, only: [:show, :edit, :update, :destroy]
before_action :set_importer, only: [:show, :entry_table, :edit, :update, :destroy]
with_themed_layout 'dashboard' if defined?(::Hyrax)

# GET /importers
Expand All @@ -34,9 +35,15 @@ def show
add_importer_breadcrumbs
add_breadcrumb @importer.name
end
@work_entries = @importer.entries.where(type: @importer.parser.entry_class.to_s).page(params[:work_entries_page]).per(30)
@collection_entries = @importer.entries.where(type: @importer.parser.collection_entry_class.to_s).page(params[:collections_entries_page]).per(30)
@file_set_entries = @importer.entries.where(type: @importer.parser.file_set_entry_class.to_s).page(params[:file_set_entries_page]).per(30)
@first_entry = @importer.entries.first
end

def entry_table
@entries = @importer.entries.order(table_order).page(table_page).per(table_per_page)
@entries = @entries.where(table_search) if table_search.present?
respond_to do |format|
format.json { render json: format_entries(@entries, @importer) }
end
end

# GET /importers/new
Expand Down Expand Up @@ -210,7 +217,7 @@ def files_for_import(file, cloud_files)

# Use callbacks to share common setup or constraints between actions.
def set_importer
@importer = Importer.find(params[:id])
@importer = Importer.find(params[:id] || params[:importer_id])
end

def importable_params
Expand Down
72 changes: 72 additions & 0 deletions app/controllers/concerns/bulkrax/datatables_behavior.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# frozen_string_literal: true

module Bulkrax
module DatatablesBehavior
extend ActiveSupport::Concern

def table_per_page
per_page = params[:length].to_i
per_page < 1 ? 30 : per_page
end

def order_value(column)
params['columns']&.[](column)&.[]('data')
end

def table_order
"#{order_value(params['order']['0']['column'])} #{params['order']['0']['dir']}" if params['order']['0']['column'].present?
end

# convert offset to page number
def table_page
params[:start].blank? ? 1 : (params[:start].to_i / params[:length].to_i) + 1
end

def table_search
return @table_search if @table_search
return @table_search = false if params['search']&.[]('value').blank?

table_search_value = params['search']&.[]('value')
@table_search = ['identifier', 'id', 'status_message', 'type', 'updated_at'].map do |col|
"cast(bulkrax_entries.#{col} as varchar(255)) ILIKE '%#{table_search_value}%'"
end.join(' OR ')
end

def format_entries(entries, item)
result = entries.map do |e|
{
identifier: view_context.link_to(e.identifier, view_context.item_entry_path(item, e)),
id: e.id,
status_message: entry_status(e),
type: e.type,
updated_at: e.updated_at,
errors: e.latest_status&.error_class&.present? ? view_context.link_to(e.latest_status.error_class, view_context.item_entry_path(item, e), title: e.latest_status.error_message) : "",
actions: util_links(e, item)
}
end
{
data: result,
recordsTotal: item.entries.size,
recordsFiltered: item.entries.size
}
end

def util_links(e, item)
links = []
links << view_context.link_to(view_context.raw('<span class="glyphicon glyphicon-info-sign"></span>'), view_context.item_entry_path(item, e))
links << "<a class='glyphicon glyphicon-repeat' data-toggle='modal' data-target='#bulkraxItemModal' data-entry-id='#{e.id}'></a>" if view_context.an_importer?(item)
links << view_context.link_to(view_context.raw('<span class="glyphicon glyphicon-trash"></span>'), view_context.item_entry_path(item, e), method: :delete, data: { confirm: 'This will delete the entry and any work associated with it. Are you sure?' })
links.join(" ")
end

def entry_status(e)
if e.status_message == "Complete"
"<td><span class='glyphicon glyphicon-ok' style='color: green;'></span> #{e.status_message}</td>"
elsif e.status_message == "Pending"
"<td><span class='glyphicon glyphicon-option-horizontal' style='color: blue;'></span> #{e.status_message}</td>"
else
"<td><span class='glyphicon glyphicon-remove' style='color: #{e.status == 'Deleted' ? 'green' : 'red'};'></span> #{e.status_message}</td>"
end
end
end
end
12 changes: 4 additions & 8 deletions app/models/bulkrax/importer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -123,16 +123,16 @@ def last_run
@last_run ||= self.importer_runs.last
end

def failed_entries?
entries.failed.any?
end

def failed_statuses
@failed_statuses ||= Bulkrax::Status.latest_by_statusable
.includes(:statusable)
.where('bulkrax_statuses.statusable_id IN (?) AND bulkrax_statuses.statusable_type = ? AND status_message = ?', self.entries.pluck(:id), 'Bulkrax::Entry', 'Failed')
end

def failed_entries
@failed_entries ||= failed_statuses.map(&:statusable)
end

def failed_messages
failed_statuses.each_with_object({}) do |e, i|
i[e.error_message] ||= []
Expand All @@ -146,10 +146,6 @@ def completed_statuses
.where('bulkrax_statuses.statusable_id IN (?) AND bulkrax_statuses.statusable_type = ? AND status_message = ?', self.entries.pluck(:id), 'Bulkrax::Entry', 'Complete')
end

def completed_entries
@completed_entries ||= completed_statuses.map(&:statusable)
end

def seen
@seen ||= {}
end
Expand Down
6 changes: 5 additions & 1 deletion app/models/bulkrax/status.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

module Bulkrax
class Status < ApplicationRecord
belongs_to :statusable, polymorphic: true
belongs_to :statusable, polymorphic: true, denormalize: { fields: %i[status_message], if: :latest? }
belongs_to :runnable, polymorphic: true
serialize :error_backtrace, Array

Expand All @@ -21,5 +21,9 @@ def self.latest_by_statusable_subtable
status_table.join(latest_status_query.as(latest_status_table.name.to_s), Arel::Nodes::InnerJoin)
.on(status_table[:id].eq(latest_status_table[:latest_status_id]))
end

def latest?
self.id == self.class.where(statusable_id: self.statusable_id, statusable_type: self.statusable_type).order('id desc').pick(:id)
end
end
end
3 changes: 3 additions & 0 deletions app/models/concerns/bulkrax/status_info.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ module StatusInfo
as: :statusable,
class_name: "Bulkrax::Status",
inverse_of: :statusable
scope :failed, -> { where(status_message: 'Failed') }
scope :complete, -> { where(status_message: 'Complete') }
scope :pending, -> { where(status_message: 'Pending') }
end

def current_status
Expand Down
22 changes: 7 additions & 15 deletions app/views/bulkrax/importers/show.html.erb
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<div class="col-xs-12 main-header">
<h1><span class="fa fa-cloud-upload" aria-hidden="true"></span> Importer: <%= @importer.name %></h1>
<% if @importer.parser_klass == 'Bulkrax::CsvParser' && @work_entries.map(&:failed?).any? %>

<% if @importer.failed_entries? %>
<div class="pull-right">
<%= link_to 'Export Errored Entries', importer_export_errors_path(@importer.id), class: 'btn btn-primary' %>
<%= link_to 'Upload Corrected Entries', importer_upload_corrected_entries_path(@importer.id), class: 'btn btn-primary' %>
<%= link_to 'Export Errored Entries', importer_export_errors_path(@importer.id), class: 'btn btn-primary', data: { turbolinks: false }%>
<%= link_to 'Upload Corrected Entries', importer_upload_corrected_entries_path(@importer.id), class: 'btn btn-primary' if @importer.parser.is_a?(Bulkrax::CsvParser) %>
</div>
<% end %>
</div>
Expand Down Expand Up @@ -75,19 +76,10 @@

<div class="bulkrax-nav-tab-bottom-margin">
<!-- Nav tabs -->
<ul class="bulkrax-nav-tab-top-margin tab-nav nav nav-tabs" role="tablist">
<li role="presentation" class="nav-link active"><a href="#work-entries" aria-controls="work-entries" role="tab" data-toggle="tab"><%= t('bulkrax.importer.labels.work_entries') %></a></li>
<li role="presentation" class="nav-link"><a href="#collection-entries" aria-controls="collection-entries" role="tab" data-toggle="tab"><%= t('bulkrax.importer.labels.collection_entries') %></a></li>
<li role="presentation" class="nav-link"><a href="#file-set-entries" aria-controls="file-set-entries" role="tab" data-toggle="tab"><%= t('bulkrax.importer.labels.file_set_entries') %></a></li>
</ul>
<!-- Tab panes -->
<div class="tab-content outline">
<%= render partial: 'bulkrax/shared/entries_tab', locals: { item: @importer, entries: @work_entries, pagination_param_name: :work_entries_page, pagination_anchor: 'work-entries' } %>
<%= render partial: 'bulkrax/shared/entries_tab', locals: { item: @importer, entries: @collection_entries, pagination_param_name: :collection_entries_path, pagination_anchor: 'collection-entries' } %>
<%= render partial: 'bulkrax/shared/entries_tab', locals: { item: @importer, entries: @file_set_entries, pagination_param_name: :file_set_entries_path, pagination_anchor: 'file-set-entries' } %>
<div class="outline">
<%= render partial: 'bulkrax/shared/entries_tab' %>
</div>
<% all_entries = @work_entries + @collection_entries + @file_set_entries %>
<%= render partial: 'bulkrax/importers/edit_item_buttons', locals: { item: @importer, e: all_entries.first } if all_entries.present? %>
<%= render partial: 'bulkrax/importers/edit_item_buttons', locals: { item: @importer, e: @first_entry } if @first_entry.present? %>
</div>

<p class="bulkrax-p-align">
Expand Down
38 changes: 5 additions & 33 deletions app/views/bulkrax/shared/_entries_tab.html.erb
Original file line number Diff line number Diff line change
@@ -1,44 +1,16 @@
<div role="tabpanel" class="tab-pane bulkrax-nav-tab-table-left-align <%= pagination_anchor == 'work-entries' ? 'active' : '' %>" id="<%= pagination_anchor %>">
<table class='table table-striped'>
<div role="tabpanel" class="tab-pane bulkrax-nav-tab-table-left-align" >
<table id='importer-show-table' class='table table-striped'>
<thead>
<tr>
<th><%= t('bulkrax.table_header.labels.identifier') %></th>
<th><%= t('bulkrax.table_header.labels.entry_id') %></th>
<th><%= t('bulkrax.table_header.labels.status') %></th>
<th><%= t('bulkrax.table_header.labels.type') %></th>
<th><%= t('bulkrax.table_header.labels.updated_at') %></th>
<th><%= t('bulkrax.table_header.labels.errors') %></th>
<th><%= t('bulkrax.table_header.labels.status_set_at') %></th>
<th><%= t('bulkrax.table_header.labels.actions') %></th>
</tr>
</thead>
<tbody>
<% entries.each do |e| %>
<tr>
<td><%= link_to e.identifier, item_entry_path(item, e) %></td>
<td><%= e.id %></td>
<% if e.status == "Complete" %>
<td><span class="glyphicon glyphicon-ok" style="color: green;"></span> <%= e.status %></td>
<% elsif e.status == "Pending" %>
<td><span class="glyphicon glyphicon-option-horizontal" style="color: blue;"></span> <%= e.status %></td>
<% else %>
<td><span class="glyphicon glyphicon-remove" style="color: <%= e.status == 'Deleted' ? 'green' : 'red' %>;"></span> <%= e.status %></td>
<% end %>
<% if e.last_error.present? %>
<td><%= link_to e.last_error.dig("error_class"), item_entry_path(item, e) %></td>
<% else %>
<td></td>
<% end %>
<td><%= e.status_at %></td>
<td>
<%= link_to raw('<span class="glyphicon glyphicon-info-sign"></span>'), item_entry_path(item, e) %>
<% if an_importer?(item) %>
<a class="glyphicon glyphicon-refresh" data-toggle="modal" data-target="#bulkraxItemModal" data-entry-id="<%= e.id %>"></a>
<% end %>
<%= link_to raw('<span class="glyphicon glyphicon-trash"></span>'), item_entry_path(item, e), method: :delete, data: { confirm: 'This will delete the entry and any work associated with it. Are you sure?' } %>
</td>
</tr>
<% end %>
</tbody>
</table>
<%= page_entries_info(entries) %><br />
<%= paginate(entries, theme: 'blacklight', param_name: pagination_param_name, params: { anchor: pagination_anchor }) %>
<div id='importer-entry-classes' class='hidden'><%= [@importer.parser.entry_class.to_s, @importer.parser.collection_entry_class.to_s, @importer.parser.file_set_entry_class.to_s].compact.join('|') %></div>
</div>
1 change: 1 addition & 0 deletions bulkrax.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Gem::Specification.new do |s|
s.add_dependency 'rails', '>= 5.1.6'
s.add_dependency 'bagit', '~> 0.4'
s.add_dependency 'coderay'
s.add_dependency 'denormalize_fields'
s.add_dependency 'iso8601', '~> 0.9.0'
s.add_dependency 'kaminari'
s.add_dependency 'language_list', '~> 1.2', '>= 1.2.1'
Expand Down
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
end
resources :importers do
put :continue
get :entry_table
get :export_errors
collection do
post :external_sets
Expand Down
7 changes: 7 additions & 0 deletions db/migrate/20240208005801_denormalize_status_message.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class DenormalizeStatusMessage < ActiveRecord::Migration[5.2]
def change
add_column :bulkrax_entries, :status_message, :string, default: 'Pending'
add_column :bulkrax_importers, :status_message, :string, default: 'Pending'
add_column :bulkrax_exporters, :status_message, :string, default: 'Pending'
end
end
1 change: 1 addition & 0 deletions lib/bulkrax.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require "bulkrax/version"
require "bulkrax/engine"
require 'active_support/all'
require 'denormalize_fields'

# rubocop:disable Metrics/ModuleLength
module Bulkrax
Expand Down
12 changes: 12 additions & 0 deletions lib/tasks/bulkrax_tasks.rake
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
# frozen_string_literal: true

namespace :bulkrax do
desc 'Update all status messages from the latest status. This is to refresh the denormalized field'
task update_status_messages: :environment do
@progress = ProgressBar.create(total: Bulkrax::Status.latest_by_statusable.count,
format: "%a %b\u{15E7}%i %c/%C %p%% %t",
progress_mark: ' ',
remainder_mark: "\u{FF65}")
Bulkrax::Status.latest_by_statusable.includes(:statusable).find_each do |status|
status.statusable.update(status_message: status.status_message)
@progress.increment
end
end

# Usage example: rails bulkrax:generate_test_csvs['5','100','GenericWork']
desc 'Generate CSVs with fake data for testing purposes'
task :generate_test_csvs, [:num_of_csvs, :csv_rows, :record_type] => :environment do |_t, args|
Expand Down
1 change: 1 addition & 0 deletions spec/test_app/config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
require "action_cable/engine"
# require "rails/test_unit/railtie"
require "sprockets/railtie"
require 'dry/monads'

Bundler.require(*Rails.groups)
require "bulkrax"
Expand Down
Loading