Skip to content

Commit 0409543

Browse files
committed
1320: refactor convert proforma to task service to use persistence for file-consistency
1 parent d27e358 commit 0409543

File tree

6 files changed

+97
-67
lines changed

6 files changed

+97
-67
lines changed

app/models/concerns/file_concern.rb

Lines changed: 0 additions & 32 deletions
This file was deleted.

app/models/model_solution.rb

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# frozen_string_literal: true
22

33
class ModelSolution < ApplicationRecord
4-
include FileConcern
54
include TransferValues
65
include ParentValidation
76

app/models/task.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
require 'nokogiri'
44
require 'zip'
55
class Task < ApplicationRecord # rubocop:disable Metrics/ClassLength
6-
include FileConcern
76
include ParentValidation
87
include TransferValues
98

@@ -23,6 +22,9 @@ class Task < ApplicationRecord # rubocop:disable Metrics/ClassLength
2322
validates :parent, presence: true, if: -> { contribution? }
2423
validate :no_license_change_on_duplicate, on: :update
2524

25+
has_many :files, as: :fileable, class_name: 'TaskFile', dependent: :destroy
26+
accepts_nested_attributes_for :files, allow_destroy: true
27+
2628
has_many :group_tasks, dependent: :destroy
2729
has_many :groups, through: :group_tasks
2830

app/models/test.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
# frozen_string_literal: true
22

33
class Test < ApplicationRecord
4-
include FileConcern
54
include TransferValues
65
include ParentValidation
76

87
belongs_to :task, autosave: true, inverse_of: :tests
98
belongs_to :testing_framework, optional: true
109
belongs_to :parent, class_name: 'Test', optional: true
10+
has_many :files, as: :fileable, class_name: 'TaskFile', dependent: :destroy
11+
accepts_nested_attributes_for :files, allow_destroy: true
1112
validates :title, presence: true
1213
validates :xml_id, presence: true
1314
validates :parent_id, uniqueness: {scope: :task}, if: -> { parent_id.present? }

app/services/proforma_service/convert_proforma_task_to_task.rb

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,15 @@ def initialize(proforma_task:, user:, task: nil)
1212
end
1313

1414
def execute
15-
import_task
15+
ActiveRecord::Base.transaction do
16+
import_task
17+
end
1618
@task
1719
end
1820

1921
private
2022

2123
def import_task
22-
upsert_files @proforma_task, @task
2324
@task.assign_attributes(
2425
user:,
2526
title: @proforma_task.title,
@@ -33,12 +34,35 @@ def import_task
3334

3435
submission_restrictions: @proforma_task.submission_restrictions,
3536
external_resources: @proforma_task.external_resources,
36-
grading_hints: @proforma_task.grading_hints,
37-
38-
tests:,
39-
model_solutions:
37+
grading_hints: @proforma_task.grading_hints
4038
)
39+
@task.save!
40+
@task.reload
41+
@task.files = @all_files # @task.files + unreferenced_files
42+
@task.reload
43+
tests
44+
@task.reload
45+
model_solutions
46+
47+
@task.reload
48+
@all_files = @task.files
49+
upsert_files @proforma_task, @task
50+
# Currently files on task do not get deleted.
51+
# find all removed files first and move only those to task (and then further as necessary) that way updated_at does not get set
52+
# upsert, then delete unwanted files (by comparing xml_ids)
4153
delete_removed_files
54+
delete_removed_objects
55+
@task.save!
56+
end
57+
58+
def all_proforma_files
59+
[@proforma_task.files + @proforma_task.tests.map(&:files) + @proforma_task.model_solutions.map(&:files)].flatten
60+
end
61+
62+
def unreferenced_files
63+
@task.all_files.filter do |f|
64+
all_proforma_files.map {|pf| pf.id.to_s }.include?(f.xml_id)
65+
end
4266
end
4367

4468
def parent_uuid
@@ -106,8 +130,9 @@ def file_attributes(proforma_task_file, fileable)
106130
end
107131

108132
def tests
109-
@proforma_task.tests.map do |test|
133+
@proforma_task.tests.each do |test|
110134
ch_test = ch_record_for(@task.tests, test.id) || Test.new(xml_id: test.id)
135+
ch_test.task = @task
111136
upsert_files test, ch_test
112137
ch_test.assign_attributes(
113138
title: test.title,
@@ -117,7 +142,7 @@ def tests
117142
meta_data: test.meta_data,
118143
configuration: test.configuration
119144
)
120-
ch_test
145+
ch_test.save!
121146
end
122147
end
123148

@@ -137,14 +162,15 @@ def programming_language # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticCo
137162
end
138163

139164
def model_solutions
140-
@proforma_task.model_solutions.map do |model_solution|
165+
@proforma_task.model_solutions.each do |model_solution|
141166
ch_model_solution = ch_record_for(@task.model_solutions, model_solution.id) || ModelSolution.new(xml_id: model_solution.id)
167+
ch_model_solution.task = @task
142168
upsert_files model_solution, ch_model_solution
143169
ch_model_solution.assign_attributes(
144170
description: model_solution.description,
145171
internal_description: model_solution.internal_description
146172
)
147-
ch_model_solution
173+
ch_model_solution.save!
148174
end
149175
end
150176

@@ -153,7 +179,14 @@ def ch_record_for(collection, xml_id)
153179
end
154180

155181
def delete_removed_files
156-
@all_files.reject {|file| @file_xml_ids.include? file.xml_id }.each(&:destroy)
182+
@all_files.reject {|file| @file_xml_ids.map(&:to_s).include? file.xml_id }.each(&:destroy)
183+
end
184+
185+
def delete_removed_objects
186+
@task.tests.reject {|test| @proforma_task.tests.map {|t| t.id.to_s }.include? test.xml_id }.each(&:destroy)
187+
@task.model_solutions.reject do |model_solution|
188+
@proforma_task.model_solutions.map {|ms| ms.id.to_s }.include? model_solution.xml_id
189+
end.each(&:destroy)
157190
end
158191
end
159192
end

spec/services/proforma_service/convert_proforma_task_to_task_spec.rb

Lines changed: 48 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
end
2525

2626
describe '#execute' do
27-
subject(:convert_to_task_service) { described_class.call(proforma_task:, user:, task:) }
27+
subject(:convert_to_task_service) { described_class.call(proforma_task:, user:, task:).reload }
2828

2929
let(:proforma_task) do
3030
ProformaXML::Task.new(
@@ -237,8 +237,9 @@
237237
end
238238

239239
it 'creates files with correct attributes' do
240-
expect(convert_to_task_service.tests[1].files[0]).to have_attributes(convert_to_task_service.tests[0].files[0].attributes.merge('xml_id' => 'id-2'))
241-
expect(convert_to_task_service.tests[2].files[0]).to have_attributes(convert_to_task_service.tests[0].files[0].attributes.merge('xml_id' => 'id-3'))
240+
attribute_exceptions = ['id', 'created_at', 'fileable_id', 'updated_at']
241+
expect(convert_to_task_service.tests[1].files[0]).to have_attributes(convert_to_task_service.tests[0].files[0].attributes.except(*attribute_exceptions).merge('xml_id' => 'id-2'))
242+
expect(convert_to_task_service.tests[2].files[0]).to have_attributes(convert_to_task_service.tests[0].files[0].attributes.except(*attribute_exceptions).merge('xml_id' => 'id-3'))
242243
end
243244

244245
it 'creates separate copies of referenced file' do
@@ -321,10 +322,12 @@
321322
internal_description: 'internal_description',
322323
test_type: 'test_type',
323324
files: test_files,
324-
meta_data: test_meta_data
325+
meta_data: test_meta_data,
326+
configuration:
325327
)
326328
end
327329

330+
let(:configuration) {}
328331
let(:test_meta_data) {}
329332
let(:test_files) { [test_file] }
330333
let(:test_file) do
@@ -402,15 +405,15 @@
402405
end
403406

404407
context 'when test has custom configuration' do
405-
let(:test) { build(:test, :with_unittest) }
408+
let(:configuration) { attributes_for(:test, :with_unittest)[:configuration] }
406409

407410
it 'creates a test with the supplied test configuration' do
408411
expect(convert_to_task_service.tests.first).to have_attributes(configuration: test.configuration)
409412
end
410413
end
411414

412415
context 'when test has multiple custom configuration' do
413-
let(:test) { build(:test, :with_multiple_custom_configurations) }
416+
let(:configuration) { attributes_for(:test, :with_multiple_custom_configurations)[:configuration] }
414417

415418
it 'creates a test with the supplied test configurations' do
416419
expect(convert_to_task_service.tests.first).to have_attributes(configuration: test.configuration)
@@ -420,7 +423,11 @@
420423
context 'when proforma_task has multiple tests' do
421424
let(:tests) { [test, test2] }
422425
let(:test2) do
423-
ProformaXML::Test.new(files: test_files2)
426+
ProformaXML::Test.new(
427+
id: 'test_file_id',
428+
title: 'wild-title',
429+
files: test_files2
430+
)
424431
end
425432
let(:test_files2) { [test_file2] }
426433
let(:test_file2) do
@@ -605,51 +612,69 @@
605612
)
606613
end
607614

615+
xit 'does not update updated_at of unchanged files' do
616+
task_file_updated_at = task_files.first.updated_at
617+
test_file_updated_at = test_files.first.updated_at
618+
ms_file_update_at = model_solution_files.first.updated_at
619+
620+
expect(convert_to_task_service).to have_attributes(
621+
622+
# files: have(1).item.and(include(have_attributes(
623+
# updated_at: task_file_updated_at,
624+
# ))),
625+
model_solutions: have(1).item.and(include(have_attributes(
626+
files: have(1).item.and(include(have_attributes(updated_at: ms_file_update_at,)))
627+
))),
628+
# tests: have(1).item.and(include(have_attributes(
629+
# id: tests.first.id,
630+
# files: have(1).item.and(include(have_attributes(updated_at: test_file_updated_at)))
631+
# )))
632+
)
633+
634+
end
635+
608636
context 'when files have been deleted' do
609637
context 'when task files have been deleted' do
610-
before { task.files = [] }
638+
before { proforma_task.files = [] }
611639

612640
it 'imports task files correctly' do
613641
expect(convert_to_task_service.files).to be_empty
614642
end
615643

616644
it 'saves the task correctly' do
617-
convert_to_task_service.save
618-
task.reload
619-
expect(task.files).to be_empty
645+
convert_to_task_service
646+
expect(task.reload.files).to be_empty
620647
end
621648
end
622649

623650
context 'when test files have been deleted' do
624-
before { task.tests.first.files = [] }
651+
before { proforma_task.tests.first.files = [] }
625652

626653
it 'imports test files correctly' do
627654
expect(convert_to_task_service.tests.first.files).to be_empty
628655
end
629656

630657
it 'saves the task correctly' do
631-
convert_to_task_service.save
632-
task.reload
658+
convert_to_task_service
633659
expect(task.tests.first.files).to be_empty
634660
end
635661
end
636662

637663
context 'when model solution files have been deleted' do
638-
before { task.model_solutions.first.files = [] }
664+
before { proforma_task.model_solutions.first.files = [] }
639665

640666
it 'imports model solution files correctly' do
641667
expect(convert_to_task_service.model_solutions.first.files).to be_empty
642668
end
643669

644670
it 'saves the task correctly' do
645-
convert_to_task_service.save
646-
task.reload
671+
convert_to_task_service
647672
expect(task.model_solutions.first.files).to be_empty
648673
end
649674
end
650675
end
651676

652-
context 'when files have been move around' do
677+
context 'when files have been moved around' do
653678
before do
654679
task_file = proforma_task.files.first
655680
test_file = proforma_task.tests.first.files.first
@@ -691,15 +716,17 @@
691716
files: have(1).item.and(include(have_attributes(id: task_files.first.id)))
692717
))),
693718
tests: have(1).item.and(include(have_attributes(
694-
id: 987_654_325,
719+
xml_id: '987654325',
695720
files: have(1).item.and(include(have_attributes(id: model_solution_files.first.id)))
696721
)))
697722
)
698723
end
699724

725+
726+
700727
context 'when imported task is persisted' do
701728
before do
702-
convert_to_task_service.save
729+
convert_to_task_service.save!
703730
task.reload
704731
end
705732

@@ -735,7 +762,7 @@
735762
files: have(1).item.and(include(have_attributes(id: task_files.first.id)))
736763
))),
737764
tests: have(1).item.and(include(have_attributes(
738-
id: 987_654_325,
765+
xml_id: '987654325',
739766
files: have(1).item.and(include(have_attributes(id: model_solution_files.first.id)))
740767
)))
741768
)

0 commit comments

Comments
 (0)