Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ bundle exec rails runner "pwd = SecureRandom.alphanumeric(16); u = User.create!(
bundle exec rails sync:assets[scan_output.applications-backup.csv]

# Run these after full assets ingest is complete.
bundle exec rails sync:cleanup_folder_assets
bundle exec rails duplicates:detect

bundle exec rails sync:post_ingest
```


Expand Down
4 changes: 4 additions & 0 deletions app/assets/stylesheets/application.css.scss
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,10 @@ div.wunderbaum div.wb-header span.wb-col.wb-active {
padding: 1px 4px;
}

.wb-tree .wb-title.wb-title-empty {
color: #dc3545;
}

.popup-select {
display: block;
width: 100%;
Expand Down
7 changes: 7 additions & 0 deletions app/javascript/controllers/wunderbaum_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,13 @@ export default class extends Controller {
titleElem.innerHTML =
`<a href="${node.data.url}" class="asset-link" target="_blank" rel="noopener" data-turbo="false">${node.title}</a>`;
}

if (isFolder) {
const isEmpty = node.data.has_descendant_assets === false;
titleElem.classList.toggle("wb-title-empty", isEmpty);
} else {
titleElem.classList.remove("wb-title-empty");
}
},

buttonClick: (e) => {
Expand Down
8 changes: 7 additions & 1 deletion app/serializers/file_tree_search_result_serializer.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
class FileTreeSearchResultSerializer < ActiveModel::Serializer
attributes :id, :folder, :parent_folder_id, :path,
:migration_status, :assigned_to_id, :assigned_to
:migration_status, :assigned_to_id, :assigned_to,
:has_descendant_assets

def folder
folder?
Expand Down Expand Up @@ -34,6 +35,11 @@ def assigned_to
object.assigned_to&.name.to_s
end

def has_descendant_assets
return nil unless folder?
object.has_descendant_assets
end

private

def folder?
Expand Down
2 changes: 1 addition & 1 deletion app/serializers/isilon_folder_serializer.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
class IsilonFolderSerializer < ActiveModel::Serializer
attributes :title, :full_path, :folder, :id, :lazy,
:assigned_to_id, :assigned_to,
:assigned_to_id, :assigned_to, :has_descendant_assets,
:parent_folder_id, :path, :key, :notes

def title
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddHasDescendantAssetsToIsilonFolders < ActiveRecord::Migration[7.2]
def change
add_column :isilon_folders, :has_descendant_assets, :boolean, null: false, default: false
end
end
3 changes: 2 additions & 1 deletion db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

73 changes: 73 additions & 0 deletions lib/tasks/isilon_import.rake
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,79 @@ namespace :sync do
puts "Deleted #{count} asset(s)"
end

desc "Backfill has_descendant_assets for folders"
task :refresh_folder_descendant_assets, [ :volume_name ] => :environment do |_t, args|
args.with_defaults(volume_name: nil)

folders = IsilonFolder.all
assets = IsilonAsset.where.not(parent_folder_id: nil)
volume = nil

if args[:volume_name].present?
volume = Volume.find_by("LOWER(name) = ?", args[:volume_name].to_s.downcase)
if volume.nil?
puts "Volume not found: #{args[:volume_name]}"
next
end

folders = folders.where(volume_id: volume.id)
assets = assets.where(volume_id: volume.id)
puts "Refreshing folder descendant flags for volume: #{volume.name} (#{volume.id})"
else
puts "Refreshing folder descendant flags across all volumes"
end

folders.update_all(has_descendant_assets: false)

if assets.none?
puts "No assets found; all folders marked empty."
next
end

volume_filter = volume ? " AND volume_id = #{volume.id}" : ""
folder_volume_filter = volume ? " AND f.volume_id = #{volume.id}" : ""
update_volume_filter = volume ? " AND volume_id = #{volume.id}" : ""

sql = <<~SQL.squish
WITH RECURSIVE ancestors AS (
SELECT parent_folder_id AS folder_id
FROM isilon_assets
WHERE parent_folder_id IS NOT NULL#{volume_filter}
UNION
SELECT f.parent_folder_id
FROM isilon_folders f
INNER JOIN ancestors a ON f.id = a.folder_id
WHERE f.parent_folder_id IS NOT NULL#{folder_volume_filter}
)
UPDATE isilon_folders
SET has_descendant_assets = TRUE
WHERE id IN (SELECT DISTINCT folder_id FROM ancestors)#{update_volume_filter}
SQL

ActiveRecord::Base.connection.execute(sql)
puts "Updated folder descendant flags."
end

desc "Post-ingest housekeeping: cleanup folder-assets, refresh empty-folder flags, detect duplicates"
task :post_ingest, [ :volume_name ] => :environment do |_t, args|
args.with_defaults(volume_name: nil)

volume_name = args[:volume_name]

puts "Running post-ingest housekeeping..."

Rake::Task["sync:cleanup_folder_assets"].reenable
Rake::Task["sync:cleanup_folder_assets"].invoke(volume_name)

Rake::Task["sync:refresh_folder_descendant_assets"].reenable
Rake::Task["sync:refresh_folder_descendant_assets"].invoke(volume_name)

Rake::Task["duplicates:detect"].reenable
Rake::Task["duplicates:detect"].invoke

puts "Post-ingest housekeeping complete."
end

desc "Export TIFF rule matches without updating migration_status"
task :tiffs_export, [ :output_path, :volume_name ] => :environment do |_t, args|
args.with_defaults(output_path: nil, volume_name: nil)
Expand Down
8 changes: 8 additions & 0 deletions spec/serializers/isilon_folder_serializer_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,12 @@

expect(serialized[:notes]).to eq("Serializer note")
end

it "includes has_descendant_assets" do
folder = create(:isilon_folder, has_descendant_assets: true)

serialized = described_class.new(folder).serializable_hash

expect(serialized[:has_descendant_assets]).to be(true)
end
end
29 changes: 28 additions & 1 deletion spec/system/folder_asset_rendering_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,18 @@
:isilon_folder,
volume: volume,
parent_folder: nil,
full_path: "RootFolder"
full_path: "RootFolder",
has_descendant_assets: true
)
end

let!(:empty_folder) do
create(
:isilon_folder,
volume: volume,
parent_folder: nil,
full_path: "EmptyFolder",
has_descendant_assets: false
)
end

Expand Down Expand Up @@ -68,4 +79,20 @@
expect(asset_row).to have_css("[data-colid='preservica_reference_id']")
expect(asset_row).to have_css("input[type='checkbox']")
end

it "adds a class for empty folders" do
empty_title = page
.all(".wb-title", visible: true)
.find { |title| title.text == "EmptyFolder" }

expect(empty_title).to be_present
expect(empty_title[:class]).to include("wb-title-empty")

non_empty_title = page
.all(".wb-title", visible: true)
.find { |title| title.text == "RootFolder" }

expect(non_empty_title).to be_present
expect(non_empty_title[:class]).not_to include("wb-title-empty")
end
end
37 changes: 36 additions & 1 deletion spec/tasks/sync_rake_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@
end

before(:each) do
Rake::Task['sync:assets'].reenable if Rake::Task.task_defined?('sync:assets')
if Rake::Task.task_defined?('sync:assets')
Rake::Task['sync:assets'].reenable
end

if Rake::Task.task_defined?('sync:refresh_folder_descendant_assets')
Rake::Task['sync:refresh_folder_descendant_assets'].reenable
end
end

describe 'sync:assets' do
Expand All @@ -30,4 +36,33 @@
end
end
end

describe 'sync:refresh_folder_descendant_assets' do
it 'marks ancestors of assets and clears empty folders' do
volume = create(:volume, name: 'Test Volume')
root_folder = create(:isilon_folder,
volume: volume,
parent_folder: nil,
full_path: '/Root',
has_descendant_assets: true)
child_folder = create(:isilon_folder,
volume: volume,
parent_folder: root_folder,
full_path: '/Root/Child',
has_descendant_assets: false)
empty_folder = create(:isilon_folder,
volume: volume,
parent_folder: nil,
full_path: '/Empty',
has_descendant_assets: true)

create(:isilon_asset, parent_folder: child_folder, volume: volume)

Rake::Task['sync:refresh_folder_descendant_assets'].invoke

expect(root_folder.reload.has_descendant_assets).to be(true)
expect(child_folder.reload.has_descendant_assets).to be(true)
expect(empty_folder.reload.has_descendant_assets).to be(false)
end
end
end