Skip to content

Commit

Permalink
Merge pull request #332 from Floppy/merge
Browse files Browse the repository at this point in the history
Model merging
  • Loading branch information
Floppy authored Dec 3, 2021
2 parents 806a0b3 + 402efb2 commit 2cb9b8c
Show file tree
Hide file tree
Showing 11 changed files with 106 additions and 11 deletions.
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,5 @@ gem "logs", "~> 0.3.0"
gem "delayed_job_active_record", "~> 4.1"

gem "activerecord-nulldb-adapter", "~> 0.8.0"

gem "memoist", "~> 0.16.2"
4 changes: 3 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@ GEM
mail (2.7.1)
mini_mime (>= 0.1.1)
marcel (1.0.1)
memoist (0.16.2)
method_source (1.0.0)
mini_mime (1.1.0)
mini_portile2 (2.6.1)
Expand Down Expand Up @@ -332,6 +333,7 @@ DEPENDENCIES
jbuilder (~> 2.11)
listen (~> 3.7)
logs (~> 0.3.0)
memoist (~> 0.16.2)
pg (~> 1.2)
public_suffix (~> 4.0)
puma (~> 5.5)
Expand All @@ -353,4 +355,4 @@ RUBY VERSION
ruby 3.0.0p0

BUNDLED WITH
2.2.4
2.2.15
9 changes: 9 additions & 0 deletions app/controllers/models_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ def update
redirect_to [@library, @model]
end

def merge
if (@parent = @model.parent)
@model.merge_into_parent!
redirect_to [@library, @parent]
else
render status: :bad_request
end
end

def bulk_edit
@creators = Creator.all
@models = @library.models
Expand Down
8 changes: 4 additions & 4 deletions app/jobs/library_scan_job.rb
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
class LibraryScanJob < ApplicationJob
queue_as :default

def self.model_pattern
lower = Rails.configuration.formats[:models].map(&:downcase)
upper = Rails.configuration.formats[:models].map(&:upcase)
def self.file_pattern
lower = Rails.configuration.formats[:models].map(&:downcase) + Rails.configuration.formats[:images].map(&:downcase)
upper = Rails.configuration.formats[:models].map(&:upcase) + Rails.configuration.formats[:images].map(&:upcase)
"*.{#{lower.zip(upper).flatten.join(",")}}"
end

def perform(library)
# For each directory in the library, create a model
all_3d_files = Dir.glob(File.join(library.path, "**", LibraryScanJob.model_pattern))
all_3d_files = Dir.glob(File.join(library.path, "**", LibraryScanJob.file_pattern))
model_folders = all_3d_files.map { |f| File.dirname(f) }.uniq
model_folders = model_folders.map { |f| f.gsub(/\/files$/, "").gsub(/\/images$/, "") }.uniq # Ignore thingiverse subfolders
model_folders.each do |path|
Expand Down
27 changes: 27 additions & 0 deletions app/models/model.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
class Model < ApplicationRecord
extend Memoist

belongs_to :library
belongs_to :creator, optional: true
has_many :parts, dependent: :destroy
Expand All @@ -17,4 +19,29 @@ def autogenerate_tags_from_path!
tag_list.add(path.split(File::SEPARATOR)[1..-2].map { |y| y.split(/[\W_+-]/).filter { |x| x.length > 1 } }.flatten)
save!
end

def parent
library.models.find_by_path File.join(File.split(path)[0..-2])
end
memoize :parent

def merge_into_parent!
return unless parent

dirname = File.split(path)[-1]
images.each do |image|
image.update(
filename: File.join(dirname, image.filename),
model: parent
)
end
parts.each do |part|
part.update(
filename: File.join(dirname, part.filename),
model: parent
)
end
reload
destroy
end
end
1 change: 1 addition & 0 deletions app/views/models/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<%= card :secondary, "Actions" do %>
<%= link_to "Edit Details", edit_library_model_path(@library, @model), class: "btn btn-primary" %>
<%= link_to "Merge Into Parent", merge_library_model_path(@library, @model), class: "btn btn-danger", method: :post if @model.parent %>
<% end %>
<%= render 'tags_card', tags: @model.tags, selected: nil %>
Expand Down
3 changes: 3 additions & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
post "/", controller: :search, action: :index
resources :libraries do
resources :models, except: [:index, :destroy] do
member do
post "merge"
end
collection do
get "edit", action: "bulk_edit"
patch "update", action: "bulk_update"
Expand Down
6 changes: 6 additions & 0 deletions spec/factories/image.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
FactoryBot.define do
factory :image do
filename { Faker::File.file_name(ext: "jpg") }
model { build :model }
end
end
Binary file not shown.
12 changes: 6 additions & 6 deletions spec/jobs/library_scan_job_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,18 @@
create(:library, path: File.join(Rails.root, "spec", "fixtures", "library"))
end

it "generates a case-insensitive pattern for model files" do
expect(LibraryScanJob.model_pattern).to eq "*.{stl,STL,obj,OBJ,3mf,3MF,blend,BLEND,mix,MIX,ply,PLY}"
it "generates a case-insensitive pattern for files" do
expect(LibraryScanJob.file_pattern).to eq "*.{stl,STL,obj,OBJ,3mf,3MF,blend,BLEND,mix,MIX,ply,PLY,jpg,JPG,png,PNG}"
end

it "can scan a library directory" do
expect { LibraryScanJob.perform_now(library) }.to change { library.models.count }.to(3)
expect(library.models.map(&:name)).to match_array ["Model One", "Model Two", "Thingiverse Model"]
expect(library.models.map(&:path)).to match_array ["/model_one", "/subfolder/model_two", "/thingiverse_model"]
expect { LibraryScanJob.perform_now(library) }.to change { library.models.count }.to(4)
expect(library.models.map(&:name)).to match_array ["Model One", "Model Two", "Nested Model", "Thingiverse Model"]
expect(library.models.map(&:path)).to match_array ["/model_one", "/subfolder/model_two", "/model_one/nested_model", "/thingiverse_model"]
end

it "queues up model scans" do
expect { LibraryScanJob.perform_now(library) }.to have_enqueued_job(ModelScanJob).exactly(3).times
expect { LibraryScanJob.perform_now(library) }.to have_enqueued_job(ModelScanJob).exactly(4).times
end

it "removes models with no parts" do
Expand Down
45 changes: 45 additions & 0 deletions spec/models/model_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

context "with a library on disk" do
before :each do
allow(File).to receive(:exist?).and_call_original
allow(File).to receive(:exist?).with("/library1").and_return(true)
allow(File).to receive(:exist?).with("/library2").and_return(true)
end
Expand All @@ -40,4 +41,48 @@
expect(build(:model, library: library2, path: "model")).to be_valid
end
end

context "nested inside another" do
before :each do
allow(File).to receive(:exist?).and_call_original
allow(File).to receive(:exist?).with("/library").and_return(true)
end

let(:library) { create(:library, path: "/library") }

it "identifies the parent" do
parent = create(:model, library: library, path: "model")
child = create(:model, library: library, path: "model/nested")
expect(child.parent).to eql parent
end

context "merging into parent" do
before :each do
@parent = create(:model, library: library, path: "model")
@child = create(:model, library: library, path: "model/nested")
end

it "moves parts" do
part = create(:part, model: @child, filename: "part.stl")
@child.merge_into_parent!
part.reload
expect(part.filename).to eql "nested/part.stl"
expect(part.model).to eql @parent
end

it "moves images" do
image = create(:image, model: @child, filename: "image.jpg")
@child.merge_into_parent!
image.reload
expect(image.filename).to eql "nested/image.jpg"
expect(image.model).to eql @parent
end

it "deletes merged model" do
expect {
@child.merge_into_parent!
}.to change { Model.count }.from(2).to(1)
end
end
end
end

0 comments on commit 2cb9b8c

Please sign in to comment.