diff --git a/app/models/code_ocean/file.rb b/app/models/code_ocean/file.rb index 0d9327531..b865909ec 100644 --- a/app/models/code_ocean/file.rb +++ b/app/models/code_ocean/file.rb @@ -60,6 +60,8 @@ class File < ApplicationRecord validates :weight, absence: true, unless: :teacher_defined_assessment? validates :file, presence: true if :context.is_a?(Submission) validates :context_type, inclusion: {in: ALLOWED_CONTEXT_TYPES} + validates :path, uniqueness: {scope: %I[name file_type context_id context_type role]} + # xml_id_path must be unique within the scope of an exercise. # Initially, it may match the record’s id (set while exporting), # but it can later diverge as long as uniqueness is preserved. diff --git a/app/services/proforma_service/convert_task_to_exercise.rb b/app/services/proforma_service/convert_task_to_exercise.rb index 238ce4eb3..083b994db 100644 --- a/app/services/proforma_service/convert_task_to_exercise.rb +++ b/app/services/proforma_service/convert_task_to_exercise.rb @@ -68,7 +68,7 @@ def string_to_bool(str) end def files - model_solution_files + test_files + task_files + (model_solution_files + test_files + task_files).uniq {|f| [f.name, f.file_type_id, f.role, f.path] } end def test_files @@ -106,16 +106,20 @@ def task_files def codeocean_file_from_task_file(file, parent_object = nil) extension = File.extname(file.filename) + path = File.dirname(file.filename).in?(['.', '']) ? nil : File.dirname(file.filename) + name = File.basename(file.filename, '.*') + role = extract_meta_data(@task.meta_data&.dig('meta-data'), 'files', "CO-#{file.id}", 'role') + # checking the last element of xml_id_path array for file.id codeocean_file = @exercise.files.detect {|f| f.xml_id_path.last == file.id } || @exercise.files.new codeocean_file.assign_attributes( context: @exercise, file_type: file_type(extension), hidden: file.visible != 'yes', # hides 'delayed' and 'no' - name: File.basename(file.filename, '.*'), + name:, read_only: file.usage_by_lms != 'edit', - role: extract_meta_data(@task.meta_data&.dig('meta-data'), 'files', "CO-#{file.id}", 'role'), - path: File.dirname(file.filename).in?(['.', '']) ? nil : File.dirname(file.filename), + role:, + path:, xml_id_path: (parent_object.nil? ? [file.id] : [parent_object.id, file.id]).map(&:to_s) ) if file.binary diff --git a/spec/models/code_ocean/file_spec.rb b/spec/models/code_ocean/file_spec.rb index e031da404..473c18022 100644 --- a/spec/models/code_ocean/file_spec.rb +++ b/spec/models/code_ocean/file_spec.rb @@ -137,4 +137,18 @@ end end end + + context 'when a file with same attributes (path, name file_type context_id context_type role) already exists' do + before do + create(:file, name: 'static', context: exercise) + file.validate + end + + let(:exercise) { create(:dummy) } + let(:file) { build(:file, name: 'static', context: exercise) } + + it 'has an error for path' do + expect(file.errors[:path]).to be_present + end + end end diff --git a/spec/services/proforma_service/convert_task_to_exercise_spec.rb b/spec/services/proforma_service/convert_task_to_exercise_spec.rb index f60cea59b..f3b3eb465 100644 --- a/spec/services/proforma_service/convert_task_to_exercise_spec.rb +++ b/spec/services/proforma_service/convert_task_to_exercise_spec.rb @@ -187,6 +187,27 @@ expect { convert_to_exercise_service.save! }.to change(Exercise, :count).by(1) end + context 'with two files with similar contents' do + let(:file_dup) do + ProformaXML::TaskFile.new( + id: 'id2', + content:, + filename:, + used_by_grader: 'used_by_grader', + visible: 'yes', + usage_by_lms:, + binary:, + mimetype: + ) + end + + let(:files) { [file, file_dup] } + + it 'creates an exercises with only one file' do + expect(convert_to_exercise_service.files.length).to be 1 + end + end + context 'when file is a Makefile' do let(:filename) { "#{path}Makefile" } @@ -346,7 +367,7 @@ ProformaXML::TaskFile.new( id: 'ms-file-2', content: 'content', - filename: 'filename.txt', + filename: 'filename2.txt', used_by_grader: 'used_by_grader', visible: 'yes', usage_by_lms: 'display', @@ -456,7 +477,7 @@ ProformaXML::TaskFile.new( id: 'test_file_id2', content: 'testfile-content', - filename: 'testfile.txt', + filename: 'testfile2.txt', used_by_grader: 'yes', visible: 'no', usage_by_lms: 'display',