From 8a7eae1a566a3646e27c8028f2e0f0c46d3e05e7 Mon Sep 17 00:00:00 2001 From: Karol Date: Mon, 24 May 2021 14:58:36 +0200 Subject: [PATCH 01/16] Migrated to proforma 0.6 Fixed all failing specs --- Gemfile | 2 +- Gemfile.lock | 6 ++-- .../convert_exercise_to_task.rb | 9 ++++-- .../convert_task_to_exercise.rb | 2 +- app/services/proforma_service/export_task.rb | 3 +- app/services/proforma_service/import.rb | 5 ++-- .../convert_exercise_to_task_spec.rb | 3 +- .../convert_task_to_exercise_spec.rb | 30 +++++++++---------- .../proforma_service/export_task_spec.rb | 2 +- spec/support/expectations/equal_exercise.rb | 6 +++- 10 files changed, 39 insertions(+), 29 deletions(-) diff --git a/Gemfile b/Gemfile index 43aa0d60..f6894d3c 100644 --- a/Gemfile +++ b/Gemfile @@ -24,7 +24,7 @@ gem 'net-http-persistent' gem 'nokogiri' gem 'pagedown-bootstrap-rails' gem 'pg' -gem 'proforma', github: 'openHPI/proforma', tag: 'v0.5.1' +gem 'proforma', github: 'openHPI/proforma', tag: 'v0.6' gem 'prometheus_exporter' gem 'pry-byebug' gem 'puma' diff --git a/Gemfile.lock b/Gemfile.lock index f7ab472a..a9141bf8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -17,10 +17,10 @@ GIT GIT remote: https://github.com/openHPI/proforma.git - revision: dc68000325388e1d75f31be9e136a82edad8a56d - tag: v0.5.1 + revision: 4b543be15c618ee3e5671fddb0e438be0278a249 + tag: v0.6 specs: - proforma (0.5) + proforma (0.6) activemodel (>= 5.2.3, < 6.2.0) activesupport (>= 5.2.3, < 6.2.0) nokogiri (>= 1.10.2, < 1.12.0) diff --git a/app/services/proforma_service/convert_exercise_to_task.rb b/app/services/proforma_service/convert_exercise_to_task.rb index 6ad3c429..655106fe 100644 --- a/app/services/proforma_service/convert_exercise_to_task.rb +++ b/app/services/proforma_service/convert_exercise_to_task.rb @@ -63,13 +63,16 @@ module ProformaService id: file.id, title: file.name, files: test_file(file), - meta_data: { - 'feedback-message' => file.feedback_message, - }.compact + meta_data: test_meta_data(file) ) end end + def test_meta_data(file) + [{namespace: 'openHPI', key: 'entry-point', value: file.filepath}, + {namespace: 'openHPI', key: 'feedback-message', value: file.feedback_message}] + end + def test_file(file) [ task_file(file).tap do |t_file| diff --git a/app/services/proforma_service/convert_task_to_exercise.rb b/app/services/proforma_service/convert_task_to_exercise.rb index 8a13f835..19a9333b 100644 --- a/app/services/proforma_service/convert_task_to_exercise.rb +++ b/app/services/proforma_service/convert_task_to_exercise.rb @@ -34,7 +34,7 @@ module ProformaService @task.tests.map do |test_object| task_files.delete(test_object.files.first.id).tap do |file| file.weight = 1.0 - file.feedback_message = test_object.meta_data['feedback-message'] + file.feedback_message = test_object.meta_data.detect {|meta_data| meta_data[:namespace] == 'openHPI' && meta_data[:key] == 'feedback-message' }[:value] end end end diff --git a/app/services/proforma_service/export_task.rb b/app/services/proforma_service/export_task.rb index ac5ed627..f1afc741 100644 --- a/app/services/proforma_service/export_task.rb +++ b/app/services/proforma_service/export_task.rb @@ -9,7 +9,8 @@ module ProformaService def execute @task = ConvertExerciseToTask.call(exercise: @exercise) - exporter = Proforma::Exporter.new(@task) + namespaces = [{prefix: 'openHPI', uri: 'open.hpi.de'}] + exporter = Proforma::Exporter.new(task: @task, custom_namespaces: namespaces) exporter.perform end end diff --git a/app/services/proforma_service/import.rb b/app/services/proforma_service/import.rb index 2aa6b6e2..2fa24383 100644 --- a/app/services/proforma_service/import.rb +++ b/app/services/proforma_service/import.rb @@ -10,8 +10,9 @@ module ProformaService def execute if single_task? - importer = Proforma::Importer.new(@zip) - @task = importer.perform + importer = Proforma::Importer.new(zip: @zip) + import_result = importer.perform + @task = import_result[:task] exercise = base_exercise exercise_files = exercise&.files&.to_a diff --git a/spec/services/proforma_service/convert_exercise_to_task_spec.rb b/spec/services/proforma_service/convert_exercise_to_task_spec.rb index e7cf793a..e28c6668 100644 --- a/spec/services/proforma_service/convert_exercise_to_task_spec.rb +++ b/spec/services/proforma_service/convert_exercise_to_task_spec.rb @@ -161,7 +161,8 @@ RSpec.describe ProformaService::ConvertExerciseToTask do id: test_file.id, title: test_file.name, files: have(1).item, - meta_data: {'feedback-message' => test_file.feedback_message} + meta_data: [{key: 'entry-point', namespace: 'openHPI', value: test_file.filepath}, + {key: 'feedback-message', namespace: 'openHPI', value: 'feedback_message'}] ) 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 05c612e4..98e8cba2 100644 --- a/spec/services/proforma_service/convert_task_to_exercise_spec.rb +++ b/spec/services/proforma_service/convert_task_to_exercise_spec.rb @@ -229,11 +229,11 @@ describe ProformaService::ConvertTaskToExercise do internal_description: 'internal_description', test_type: 'test_type', files: test_files, - meta_data: { - 'feedback-message' => 'feedback-message', - 'testing-framework' => 'testing-framework', - 'testing-framework-version' => 'testing-framework-version', - } + meta_data: [ + {namespace: 'openHPI', key: 'feedback-message', value: 'feedback-message'}, + {namespace: 'openHPI', key: 'testing-framework', value: 'testing-framework'}, + {namespace: 'openHPI', key: 'testing-framework-version', value: 'testing-framework-version'}, + ] ) end @@ -272,11 +272,11 @@ describe ProformaService::ConvertTaskToExercise do let(:test2) do Proforma::Test.new( files: test_files2, - meta_data: { - 'feedback-message' => 'feedback-message', - 'testing-framework' => 'testing-framework', - 'testing-framework-version' => 'testing-framework-version', - } + meta_data: [ + {namespace: 'openHPI', key: 'feedback-message', value: 'feedback-message'}, + {namespace: 'openHPI', key: 'testing-framework', value: 'testing-framework'}, + {namespace: 'openHPI', key: 'testing-framework-version', value: 'testing-framework-version'}, + ] ) end let(:test_files2) { [test_file2] } @@ -352,11 +352,11 @@ describe ProformaService::ConvertTaskToExercise do internal_description: 'regular_file', test_type: 'test_type', files: test_files, - meta_data: { - 'feedback-message' => 'feedback-message', - 'testing-framework' => 'testing-framework', - 'testing-framework-version' => 'testing-framework-version', - } + meta_data: [ + {namespace: 'openHPI', key: 'feedback-message', value: 'feedback-message'}, + {namespace: 'openHPI', key: 'testing-framework', value: 'testing-framework'}, + {namespace: 'openHPI', key: 'testing-framework-version', value: 'testing-framework-version'}, + ] ) end let(:test_files) { [test_file] } diff --git a/spec/services/proforma_service/export_task_spec.rb b/spec/services/proforma_service/export_task_spec.rb index 157094de..f91423ca 100644 --- a/spec/services/proforma_service/export_task_spec.rb +++ b/spec/services/proforma_service/export_task_spec.rb @@ -30,7 +30,7 @@ describe ProformaService::ExportTask do before do allow(ProformaService::ConvertExerciseToTask).to receive(:call).with(exercise: exercise).and_return(task) - allow(Proforma::Exporter).to receive(:new).with(task).and_return(exporter) + allow(Proforma::Exporter).to receive(:new).with(task: task, custom_namespaces: [{prefix: 'openHPI', uri: 'open.hpi.de'}]).and_return(exporter) end it do diff --git a/spec/support/expectations/equal_exercise.rb b/spec/support/expectations/equal_exercise.rb index 5cdaece9..289941f5 100644 --- a/spec/support/expectations/equal_exercise.rb +++ b/spec/support/expectations/equal_exercise.rb @@ -31,7 +31,11 @@ RSpec::Matchers.define :be_an_equal_exercise_as do |exercise| return true if object == other # for [] return false if object.length != other.length - object.to_a.product(other.to_a).map {|k, v| equal?(k, v) }.any? + object.map do |element| + other.map {|other_element| equal?(element, other_element) }.any? + end.all? && other.map do |element| + object.map {|other_element| equal?(element, other_element) }.any? + end.all? end def attributes_and_associations(object) From d32eb0fc4d21add57e173d8fc879c38bdf24fcc8 Mon Sep 17 00:00:00 2001 From: Karol Date: Sat, 27 Nov 2021 21:08:07 +0100 Subject: [PATCH 02/16] bump proforma-gem --- Gemfile | 2 +- Gemfile.lock | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Gemfile b/Gemfile index f6894d3c..677b072a 100644 --- a/Gemfile +++ b/Gemfile @@ -24,7 +24,7 @@ gem 'net-http-persistent' gem 'nokogiri' gem 'pagedown-bootstrap-rails' gem 'pg' -gem 'proforma', github: 'openHPI/proforma', tag: 'v0.6' +gem 'proforma', github: 'openHPI/proforma', tag: 'v0.7' gem 'prometheus_exporter' gem 'pry-byebug' gem 'puma' diff --git a/Gemfile.lock b/Gemfile.lock index a9141bf8..fa99761a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -15,15 +15,13 @@ GIT rack (>= 1.5.0) websocket (>= 1.1.0) -GIT - remote: https://github.com/openHPI/proforma.git - revision: 4b543be15c618ee3e5671fddb0e438be0278a249 - tag: v0.6 +PATH + remote: ../proforma specs: - proforma (0.6) + proforma (0.7) activemodel (>= 5.2.3, < 6.2.0) activesupport (>= 5.2.3, < 6.2.0) - nokogiri (>= 1.10.2, < 1.12.0) + nokogiri (>= 1.10.2, < 1.13.0) rubyzip (>= 1.2.2, < 2.4.0) GEM From 2c7748ed12cc22c04697457a83f7310ae2e535b2 Mon Sep 17 00:00:00 2001 From: Karol Date: Sat, 27 Nov 2021 21:08:52 +0100 Subject: [PATCH 03/16] fix namespacing, specs --- .../convert_exercise_to_task.rb | 15 ++++-- .../convert_task_to_exercise.rb | 4 +- .../convert_exercise_to_task_spec.rb | 15 ++++-- .../convert_task_to_exercise_spec.rb | 49 +++++++++++-------- .../proforma_service/export_task_spec.rb | 1 + 5 files changed, 56 insertions(+), 28 deletions(-) diff --git a/app/services/proforma_service/convert_exercise_to_task.rb b/app/services/proforma_service/convert_exercise_to_task.rb index 655106fe..5c87f50e 100644 --- a/app/services/proforma_service/convert_exercise_to_task.rb +++ b/app/services/proforma_service/convert_exercise_to_task.rb @@ -22,12 +22,19 @@ module ProformaService { title: @exercise.title, description: @exercise.description, - internal_description: @exercise.instructions, + internal_description: nil, + # ?: @exercise.instructions, store in meta-data + # proglang: proglang, where can we get this information? files: task_files, tests: tests, uuid: uuid, language: DEFAULT_LANGUAGE, model_solutions: model_solutions, + meta_data: { + openHPI: { + instructions: @exercise.instructions, + }, + }, }.compact ) end @@ -69,8 +76,10 @@ module ProformaService end def test_meta_data(file) - [{namespace: 'openHPI', key: 'entry-point', value: file.filepath}, - {namespace: 'openHPI', key: 'feedback-message', value: file.feedback_message}] + {openHPI: { + 'entry-point': file.filepath, + 'feedback-message': file.feedback_message, + }} end def test_file(file) diff --git a/app/services/proforma_service/convert_task_to_exercise.rb b/app/services/proforma_service/convert_task_to_exercise.rb index 19a9333b..e761efb5 100644 --- a/app/services/proforma_service/convert_task_to_exercise.rb +++ b/app/services/proforma_service/convert_task_to_exercise.rb @@ -21,7 +21,7 @@ module ProformaService user: @user, title: @task.title, description: @task.description, - instructions: @task.internal_description, + instructions: @task.meta_data&.dig(:openHPI)&.dig(:instructions), files: files ) end @@ -34,7 +34,7 @@ module ProformaService @task.tests.map do |test_object| task_files.delete(test_object.files.first.id).tap do |file| file.weight = 1.0 - file.feedback_message = test_object.meta_data.detect {|meta_data| meta_data[:namespace] == 'openHPI' && meta_data[:key] == 'feedback-message' }[:value] + file.feedback_message = test_object.meta_data[:openHPI]&.dig(:'feedback-message') end end end diff --git a/spec/services/proforma_service/convert_exercise_to_task_spec.rb b/spec/services/proforma_service/convert_exercise_to_task_spec.rb index e28c6668..a8f277bb 100644 --- a/spec/services/proforma_service/convert_exercise_to_task_spec.rb +++ b/spec/services/proforma_service/convert_exercise_to_task_spec.rb @@ -30,13 +30,18 @@ RSpec.describe ProformaService::ConvertExerciseToTask do expect(task).to have_attributes( title: exercise.title, description: exercise.description, - internal_description: exercise.instructions, + # internal_description: exercise.instructions, # proglang: { # name: exercise.execution_environment.language, # version: exercise.execution_environment.version # }, uuid: exercise.uuid, language: described_class::DEFAULT_LANGUAGE, + meta_data: { + openHPI: { + instructions: exercise.instructions, + }, + }, # parent_uuid: exercise.clone_relations.first&.origin&.uuid, files: [], tests: [], @@ -161,8 +166,12 @@ RSpec.describe ProformaService::ConvertExerciseToTask do id: test_file.id, title: test_file.name, files: have(1).item, - meta_data: [{key: 'entry-point', namespace: 'openHPI', value: test_file.filepath}, - {key: 'feedback-message', namespace: 'openHPI', value: 'feedback_message'}] + meta_data: { + openHPI: { + 'entry-point': test_file.filepath, + 'feedback-message': 'feedback_message', + }, + } ) 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 98e8cba2..b357df01 100644 --- a/spec/services/proforma_service/convert_task_to_exercise_spec.rb +++ b/spec/services/proforma_service/convert_task_to_exercise_spec.rb @@ -34,11 +34,15 @@ describe ProformaService::ConvertTaskToExercise do Proforma::Task.new( title: 'title', description: 'description', - internal_description: 'internal_description', - proglang: {name: 'proglang-name', version: 'proglang-version'}, + # proglang: {name: 'proglang-name', version: 'proglang-version'}, uuid: 'uuid', parent_uuid: 'parent_uuid', language: 'language', + meta_data: { + openHPI: { + instructions: 'instructions', + }, + }, model_solutions: model_solutions, files: files, tests: tests @@ -54,7 +58,7 @@ describe ProformaService::ConvertTaskToExercise do expect(convert_to_exercise_service).to have_attributes( title: 'title', description: 'description', - instructions: 'internal_description', + instructions: 'instructions', execution_environment: be_blank, uuid: be_blank, unpublished: true, @@ -226,14 +230,15 @@ describe ProformaService::ConvertTaskToExercise do id: 'test-id', title: 'title', description: 'description', - internal_description: 'internal_description', test_type: 'test_type', files: test_files, - meta_data: [ - {namespace: 'openHPI', key: 'feedback-message', value: 'feedback-message'}, - {namespace: 'openHPI', key: 'testing-framework', value: 'testing-framework'}, - {namespace: 'openHPI', key: 'testing-framework-version', value: 'testing-framework-version'}, - ] + meta_data: { + openHPI: { + 'feedback-message': 'feedback-message', + 'testing-framework': 'testing-framework', + 'testing-framework-version': 'testing-framework-version', + }, + } ) end @@ -272,11 +277,13 @@ describe ProformaService::ConvertTaskToExercise do let(:test2) do Proforma::Test.new( files: test_files2, - meta_data: [ - {namespace: 'openHPI', key: 'feedback-message', value: 'feedback-message'}, - {namespace: 'openHPI', key: 'testing-framework', value: 'testing-framework'}, - {namespace: 'openHPI', key: 'testing-framework-version', value: 'testing-framework-version'}, - ] + meta_data: { + openHPI: { + 'feedback-message': 'feedback-message', + 'testing-framework': 'testing-framework', + 'testing-framework-version': 'testing-framework-version', + }, + } ) end let(:test_files2) { [test_file2] } @@ -317,7 +324,7 @@ describe ProformaService::ConvertTaskToExercise do id: exercise.id, title: task.title, description: task.description, - instructions: task.internal_description, + instructions: task.meta_data[:openHPI][:instructions], execution_environment: exercise.execution_environment, uuid: exercise.uuid, user: exercise.user, @@ -352,11 +359,13 @@ describe ProformaService::ConvertTaskToExercise do internal_description: 'regular_file', test_type: 'test_type', files: test_files, - meta_data: [ - {namespace: 'openHPI', key: 'feedback-message', value: 'feedback-message'}, - {namespace: 'openHPI', key: 'testing-framework', value: 'testing-framework'}, - {namespace: 'openHPI', key: 'testing-framework-version', value: 'testing-framework-version'}, - ] + meta_data: { + openHPI: { + 'feedback-message': 'feedback-message', + 'testing-framework': 'testing-framework', + 'testing-framework-version': 'testing-framework-version', + }, + } ) end let(:test_files) { [test_file] } diff --git a/spec/services/proforma_service/export_task_spec.rb b/spec/services/proforma_service/export_task_spec.rb index f91423ca..3c2b4f26 100644 --- a/spec/services/proforma_service/export_task_spec.rb +++ b/spec/services/proforma_service/export_task_spec.rb @@ -37,5 +37,6 @@ describe ProformaService::ExportTask do export_task expect(exporter).to have_received(:perform) end + # TODO more tests?! end end From ec49f91a08ac0ed31ba3f7b2c1c5d1e52ea21731 Mon Sep 17 00:00:00 2001 From: Karol Date: Mon, 6 Dec 2021 20:23:30 +0100 Subject: [PATCH 04/16] change namespace to CodeOcean --- .../proforma_service/convert_exercise_to_task.rb | 12 +++++++----- .../proforma_service/convert_task_to_exercise.rb | 4 ++-- app/services/proforma_service/export_task.rb | 2 +- .../convert_exercise_to_task_spec.rb | 4 ++-- .../convert_task_to_exercise_spec.rb | 10 +++++----- spec/services/proforma_service/export_task_spec.rb | 2 +- 6 files changed, 18 insertions(+), 16 deletions(-) diff --git a/app/services/proforma_service/convert_exercise_to_task.rb b/app/services/proforma_service/convert_exercise_to_task.rb index 5c87f50e..c2ff1bbf 100644 --- a/app/services/proforma_service/convert_exercise_to_task.rb +++ b/app/services/proforma_service/convert_exercise_to_task.rb @@ -31,7 +31,7 @@ module ProformaService language: DEFAULT_LANGUAGE, model_solutions: model_solutions, meta_data: { - openHPI: { + CodeOcean: { instructions: @exercise.instructions, }, }, @@ -76,10 +76,12 @@ module ProformaService end def test_meta_data(file) - {openHPI: { - 'entry-point': file.filepath, - 'feedback-message': file.feedback_message, - }} + { + CodeOcean: { + 'entry-point': file.filepath, + 'feedback-message': file.feedback_message, + }, + } end def test_file(file) diff --git a/app/services/proforma_service/convert_task_to_exercise.rb b/app/services/proforma_service/convert_task_to_exercise.rb index e761efb5..206bda18 100644 --- a/app/services/proforma_service/convert_task_to_exercise.rb +++ b/app/services/proforma_service/convert_task_to_exercise.rb @@ -21,7 +21,7 @@ module ProformaService user: @user, title: @task.title, description: @task.description, - instructions: @task.meta_data&.dig(:openHPI)&.dig(:instructions), + instructions: @task.meta_data&.dig(:CodeOcean)&.dig(:instructions), files: files ) end @@ -34,7 +34,7 @@ module ProformaService @task.tests.map do |test_object| task_files.delete(test_object.files.first.id).tap do |file| file.weight = 1.0 - file.feedback_message = test_object.meta_data[:openHPI]&.dig(:'feedback-message') + file.feedback_message = test_object.meta_data[:CodeOcean]&.dig(:'feedback-message') end end end diff --git a/app/services/proforma_service/export_task.rb b/app/services/proforma_service/export_task.rb index f1afc741..ca97f1c7 100644 --- a/app/services/proforma_service/export_task.rb +++ b/app/services/proforma_service/export_task.rb @@ -9,7 +9,7 @@ module ProformaService def execute @task = ConvertExerciseToTask.call(exercise: @exercise) - namespaces = [{prefix: 'openHPI', uri: 'open.hpi.de'}] + namespaces = [{prefix: 'CodeOcean', uri: 'codeocean.openhpi.de'}] exporter = Proforma::Exporter.new(task: @task, custom_namespaces: namespaces) exporter.perform end diff --git a/spec/services/proforma_service/convert_exercise_to_task_spec.rb b/spec/services/proforma_service/convert_exercise_to_task_spec.rb index a8f277bb..87d90b90 100644 --- a/spec/services/proforma_service/convert_exercise_to_task_spec.rb +++ b/spec/services/proforma_service/convert_exercise_to_task_spec.rb @@ -38,7 +38,7 @@ RSpec.describe ProformaService::ConvertExerciseToTask do uuid: exercise.uuid, language: described_class::DEFAULT_LANGUAGE, meta_data: { - openHPI: { + CodeOcean: { instructions: exercise.instructions, }, }, @@ -167,7 +167,7 @@ RSpec.describe ProformaService::ConvertExerciseToTask do title: test_file.name, files: have(1).item, meta_data: { - openHPI: { + CodeOcean: { 'entry-point': test_file.filepath, 'feedback-message': 'feedback_message', }, 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 b357df01..cca0b967 100644 --- a/spec/services/proforma_service/convert_task_to_exercise_spec.rb +++ b/spec/services/proforma_service/convert_task_to_exercise_spec.rb @@ -39,7 +39,7 @@ describe ProformaService::ConvertTaskToExercise do parent_uuid: 'parent_uuid', language: 'language', meta_data: { - openHPI: { + CodeOcean: { instructions: 'instructions', }, }, @@ -233,7 +233,7 @@ describe ProformaService::ConvertTaskToExercise do test_type: 'test_type', files: test_files, meta_data: { - openHPI: { + CodeOcean: { 'feedback-message': 'feedback-message', 'testing-framework': 'testing-framework', 'testing-framework-version': 'testing-framework-version', @@ -278,7 +278,7 @@ describe ProformaService::ConvertTaskToExercise do Proforma::Test.new( files: test_files2, meta_data: { - openHPI: { + CodeOcean: { 'feedback-message': 'feedback-message', 'testing-framework': 'testing-framework', 'testing-framework-version': 'testing-framework-version', @@ -324,7 +324,7 @@ describe ProformaService::ConvertTaskToExercise do id: exercise.id, title: task.title, description: task.description, - instructions: task.meta_data[:openHPI][:instructions], + instructions: task.meta_data[:CodeOcean][:instructions], execution_environment: exercise.execution_environment, uuid: exercise.uuid, user: exercise.user, @@ -360,7 +360,7 @@ describe ProformaService::ConvertTaskToExercise do test_type: 'test_type', files: test_files, meta_data: { - openHPI: { + CodeOcean: { 'feedback-message': 'feedback-message', 'testing-framework': 'testing-framework', 'testing-framework-version': 'testing-framework-version', diff --git a/spec/services/proforma_service/export_task_spec.rb b/spec/services/proforma_service/export_task_spec.rb index 3c2b4f26..331a7ec7 100644 --- a/spec/services/proforma_service/export_task_spec.rb +++ b/spec/services/proforma_service/export_task_spec.rb @@ -30,7 +30,7 @@ describe ProformaService::ExportTask do before do allow(ProformaService::ConvertExerciseToTask).to receive(:call).with(exercise: exercise).and_return(task) - allow(Proforma::Exporter).to receive(:new).with(task: task, custom_namespaces: [{prefix: 'openHPI', uri: 'open.hpi.de'}]).and_return(exporter) + allow(Proforma::Exporter).to receive(:new).with(task: task, custom_namespaces: [{prefix: 'CodeOcean', uri: 'codeocean.openhpi.de'}]).and_return(exporter) end it do From a006a477e075db9016535ea6eb8530c66d7ebbf8 Mon Sep 17 00:00:00 2001 From: Karol Date: Thu, 9 Dec 2021 19:57:21 +0100 Subject: [PATCH 05/16] update proforma gem version --- Gemfile.lock | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index fa99761a..e2122867 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -15,14 +15,16 @@ GIT rack (>= 1.5.0) websocket (>= 1.1.0) -PATH - remote: ../proforma +GIT + remote: https://github.com/openHPI/proforma.git + revision: ff25d4994774af3ad6f55d94cd5192ea3d4ecba2 + tag: v0.7 specs: proforma (0.7) - activemodel (>= 5.2.3, < 6.2.0) - activesupport (>= 5.2.3, < 6.2.0) - nokogiri (>= 1.10.2, < 1.13.0) - rubyzip (>= 1.2.2, < 2.4.0) + activemodel (>= 5.2.3, < 7.0.0) + activesupport (>= 5.2.3, < 7.0.0) + nokogiri (>= 1.10.2, < 2.0.0) + rubyzip (>= 1.2.2, < 3.0.0) GEM remote: https://rubygems.org/ From 886a5b68f670cb7e876174a461574a402c91c090 Mon Sep 17 00:00:00 2001 From: Karol Date: Thu, 9 Dec 2021 20:36:57 +0100 Subject: [PATCH 06/16] update gem fix codeharbor endpoint usage and translations --- Gemfile | 2 +- Gemfile.lock | 10 +++++----- app/controllers/exercises_controller.rb | 2 +- app/services/exercise_service/check_external.rb | 13 ++++++------- config/locales/de.yml | 6 +++--- config/locales/en.yml | 6 +++--- .../exercise_service/check_external_spec.rb | 16 ++++++++-------- .../proforma_service/export_task_spec.rb | 1 - 8 files changed, 27 insertions(+), 29 deletions(-) diff --git a/Gemfile b/Gemfile index 677b072a..c761f07e 100644 --- a/Gemfile +++ b/Gemfile @@ -24,7 +24,7 @@ gem 'net-http-persistent' gem 'nokogiri' gem 'pagedown-bootstrap-rails' gem 'pg' -gem 'proforma', github: 'openHPI/proforma', tag: 'v0.7' +gem 'proforma', github: 'openHPI/proforma', tag: 'v0.7.1' gem 'prometheus_exporter' gem 'pry-byebug' gem 'puma' diff --git a/Gemfile.lock b/Gemfile.lock index e2122867..e4ebea24 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -17,12 +17,12 @@ GIT GIT remote: https://github.com/openHPI/proforma.git - revision: ff25d4994774af3ad6f55d94cd5192ea3d4ecba2 - tag: v0.7 + revision: cf61517a5cd765afb9d0d19ea1c692e18e3131d7 + tag: v0.7.1 specs: - proforma (0.7) - activemodel (>= 5.2.3, < 7.0.0) - activesupport (>= 5.2.3, < 7.0.0) + proforma (0.7.1) + activemodel (>= 5.2.3, < 8.0.0) + activesupport (>= 5.2.3, < 8.0.0) nokogiri (>= 1.10.2, < 2.0.0) rubyzip (>= 1.2.2, < 3.0.0) diff --git a/app/controllers/exercises_controller.rb b/app/controllers/exercises_controller.rb index 7e0a5435..37be0aa2 100644 --- a/app/controllers/exercises_controller.rb +++ b/app/controllers/exercises_controller.rb @@ -118,7 +118,7 @@ raise: false partial: 'export_actions', locals: { exercise: @exercise, - exercise_found: codeharbor_check[:exercise_found], + exercise_found: codeharbor_check[:task_found], update_right: codeharbor_check[:update_right], error: codeharbor_check[:error], exported: false, diff --git a/app/services/exercise_service/check_external.rb b/app/services/exercise_service/check_external.rb index 79836a44..ab7f0db9 100644 --- a/app/services/exercise_service/check_external.rb +++ b/app/services/exercise_service/check_external.rb @@ -14,21 +14,20 @@ module ExerciseService req.headers['Authorization'] = "Bearer #{@codeharbor_link.api_key}" req.body = {uuid: @uuid}.to_json end - response_hash = JSON.parse(response.body, symbolize_names: true).slice(:exercise_found, :update_right) + response_hash = JSON.parse(response.body, symbolize_names: true).slice(:task_found, :update_right) - {error: false, -message: message(response_hash[:exercise_found], response_hash[:update_right])}.merge(response_hash) + {error: false, message: message(response_hash[:task_found], response_hash[:update_right])}.merge(response_hash) rescue Faraday::Error, JSON::ParserError {error: true, message: I18n.t('exercises.export_codeharbor.error')} end private - def message(exercise_found, update_right) - if exercise_found - update_right ? I18n.t('exercises.export_codeharbor.check.exercise_found') : I18n.t('exercises.export_codeharbor.check.exercise_found_no_right') + def message(task_found, update_right) + if task_found + update_right ? I18n.t('exercises.export_codeharbor.check.task_found') : I18n.t('exercises.export_codeharbor.check.task_found_no_right') else - I18n.t('exercises.export_codeharbor.check.no_exercise') + I18n.t('exercises.export_codeharbor.check.no_task') end end diff --git a/config/locales/de.yml b/config/locales/de.yml index ba767d3a..bbbd11a2 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -383,9 +383,9 @@ de: close: Schließen abort: Abbrechen check: - no_exercise: Auf CodeHarbor wurde keine entsprechende Aufgabe gefunden. Mit dem Export der Aufgabe wird eine neue auf CodeHarbor angelegt, die mit dieser verbunden ist. Anschließend können Veränderungen an der Aufgabe von beiden Systemen aus jeweils in das andere Übertragen werden. - exercise_found: Auf CodeHarbor wurde eine entsprechende Aufgabe gefunden. Mit dem Export der Aufgabe werden alle Veränderungen, die auf Codeocean vorgenommen wurden, exportiert und die Aufgabe auf CodeHarbor überschrieben. - exercise_found_no_right: Auf CodeHarbor wurde eine entsprechende Aufgabe gefunden, Sie haben aber keine Rechte sie zu bearbeiten. Bitte wenden Sie sich an einen Admin, wenn Sie denken, dass Sie die Rechte dazu besitzen sollten. + no_task: Auf CodeHarbor wurde keine entsprechende Aufgabe gefunden. Mit dem Export der Aufgabe wird eine neue auf CodeHarbor angelegt, die mit dieser verbunden ist. Anschließend können Veränderungen an der Aufgabe von beiden Systemen aus jeweils in das andere Übertragen werden. + task_found: Auf CodeHarbor wurde eine entsprechende Aufgabe gefunden. Mit dem Export der Aufgabe werden alle Veränderungen, die auf Codeocean vorgenommen wurden, exportiert und die Aufgabe auf CodeHarbor überschrieben. + task_found_no_right: Auf CodeHarbor wurde eine entsprechende Aufgabe gefunden, Sie haben aber keine Rechte sie zu bearbeiten. Bitte wenden Sie sich an einen Admin, wenn Sie denken, dass Sie die Rechte dazu besitzen sollten. file_form: hints: feedback_message: Diese Nachricht wird als Hinweis zu fehlschlagenden Tests angezeigt. diff --git a/config/locales/en.yml b/config/locales/en.yml index 33eb6adc..a40632a5 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -383,9 +383,9 @@ en: close: Close abort: Abort check: - no_exercise: No corresponding exercise found on CodeHarbor. Pushing this exercise will create a new exercise on CodeHarbor, which will be linked to this one on Codeocean. Any changes to either one can be pushed to the respective other platform. - exercise_found: 'A corresponding exercise has been found on CodeHarbor. You can export the exercise to transfer all changes made on Codeocean to CodeHarbor. Careful: This will overwrite all potential changes made on CodeHarbor.' - exercise_found_no_right: A corresponding exercise has been found on CodeHarbor, but you don't have the rights to edit it. Please contact an Admin if you think you should be able to edit the exercise on CodeHarbor. + no_task: No corresponding task found on CodeHarbor. Pushing this exercise will create a new task on CodeHarbor, which will be linked to this one on Codeocean. Any changes to either one can be pushed to the respective other platform. + task_found: 'A corresponding task has been found on CodeHarbor. You can export the exercise to transfer all changes made on Codeocean to Codeharbor. Careful: This will overwrite all potential changes made on Codeharbor.' + task_found_no_right: A corresponding task has been found on CodeHarbor, but you don't have the rights to edit it. Please contact an Admin if you think you should be able to edit the task on CodeHarbor. file_form: hints: feedback_message: This message is used as a hint for failing tests. diff --git a/spec/services/exercise_service/check_external_spec.rb b/spec/services/exercise_service/check_external_spec.rb index f5ee4b97..a4b0997c 100644 --- a/spec/services/exercise_service/check_external_spec.rb +++ b/spec/services/exercise_service/check_external_spec.rb @@ -42,25 +42,25 @@ describe ExerciseService::CheckExternal do end context 'when response contains a JSON with expected keys' do - let(:response) { {exercise_found: true, update_right: true}.to_json } + let(:response) { {task_found: true, update_right: true}.to_json } it 'returns the correct hash' do - expect(check_external_service).to eql(error: false, message: I18n.t('exercises.export_codeharbor.check.exercise_found'), exercise_found: true, update_right: true) + expect(check_external_service).to eql(error: false, message: I18n.t('exercises.export_codeharbor.check.task_found'), task_found: true, update_right: true) end - context 'with exercise_found: false and no update_right' do - let(:response) { {exercise_found: false}.to_json } + context 'with task_found: false and no update_right' do + let(:response) { {task_found: false}.to_json } it 'returns the correct hash' do - expect(check_external_service).to eql(error: false, message: I18n.t('exercises.export_codeharbor.check.no_exercise'), exercise_found: false) + expect(check_external_service).to eql(error: false, message: I18n.t('exercises.export_codeharbor.check.no_task'), task_found: false) end end - context 'with exercise_found: true and update_right: false' do - let(:response) { {exercise_found: true, update_right: false}.to_json } + context 'with task_found: true and update_right: false' do + let(:response) { {task_found: true, update_right: false}.to_json } it 'returns the correct hash' do - expect(check_external_service).to eql(error: false, message: I18n.t('exercises.export_codeharbor.check.exercise_found_no_right'), exercise_found: true, update_right: false) + expect(check_external_service).to eql(error: false, message: I18n.t('exercises.export_codeharbor.check.task_found_no_right'), task_found: true, update_right: false) end end end diff --git a/spec/services/proforma_service/export_task_spec.rb b/spec/services/proforma_service/export_task_spec.rb index 331a7ec7..1d010a2f 100644 --- a/spec/services/proforma_service/export_task_spec.rb +++ b/spec/services/proforma_service/export_task_spec.rb @@ -37,6 +37,5 @@ describe ProformaService::ExportTask do export_task expect(exporter).to have_received(:perform) end - # TODO more tests?! end end From 3260d8f82fa1c0c024a91238e73171de24874ae4 Mon Sep 17 00:00:00 2001 From: Karol Date: Tue, 11 Jan 2022 22:17:09 +0100 Subject: [PATCH 07/16] tiny refactor from exercise_found to task_found --- app/controllers/exercises_controller.rb | 2 +- app/views/exercises/_export_actions.html.slim | 2 +- spec/controllers/exercises_controller_spec.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/controllers/exercises_controller.rb b/app/controllers/exercises_controller.rb index 37be0aa2..54600a4d 100644 --- a/app/controllers/exercises_controller.rb +++ b/app/controllers/exercises_controller.rb @@ -118,7 +118,7 @@ raise: false partial: 'export_actions', locals: { exercise: @exercise, - exercise_found: codeharbor_check[:task_found], + task_found: codeharbor_check[:task_found], update_right: codeharbor_check[:update_right], error: codeharbor_check[:error], exported: false, diff --git a/app/views/exercises/_export_actions.html.slim b/app/views/exercises/_export_actions.html.slim index 0484d2b2..14af1f34 100644 --- a/app/views/exercises/_export_actions.html.slim +++ b/app/views/exercises/_export_actions.html.slim @@ -4,7 +4,7 @@ = t('exercises.export_codeharbor.buttons.retry') - else - unless exported - - if !exercise_found || update_right + - if !task_found || update_right = button_tag type: 'button', class:'btn btn-primary pull-right export-action export-button', data: {exercise_id: exercise.id} do i.fa.fa-check.confirm-icon = t('exercises.export_codeharbor.buttons.export') diff --git a/spec/controllers/exercises_controller_spec.rb b/spec/controllers/exercises_controller_spec.rb index 683f0d40..e6be8986 100644 --- a/spec/controllers/exercises_controller_spec.rb +++ b/spec/controllers/exercises_controller_spec.rb @@ -353,7 +353,7 @@ describe ExercisesController do let(:post_request) { post :export_external_check, params: {id: exercise.id} } let!(:codeharbor_link) { FactoryBot.create(:codeharbor_link, user: user) } - let(:external_check_hash) { {message: message, exercise_found: true, update_right: update_right, error: error} } + let(:external_check_hash) { {message: message, task_found: true, update_right: update_right, error: error} } let(:message) { 'message' } let(:update_right) { true } let(:error) { nil } From 6094767a654265fafe7c1409e96267892d3b4d04 Mon Sep 17 00:00:00 2001 From: Karol Date: Sat, 20 Aug 2022 22:17:20 +0200 Subject: [PATCH 08/16] - rename parameter to be more generic - fix splatting of service args - add file role depending on parent object of file - add default feedback_message for imports --- app/controllers/exercises_controller.rb | 8 ++++---- .../proforma_service/convert_task_to_exercise.rb | 15 ++++++++++++--- app/services/service_base.rb | 4 ++-- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/app/controllers/exercises_controller.rb b/app/controllers/exercises_controller.rb index b484d4b6..f3de2c20 100644 --- a/app/controllers/exercises_controller.rb +++ b/app/controllers/exercises_controller.rb @@ -118,7 +118,7 @@ raise: false partial: 'export_actions', locals: { exercise: @exercise, - task_found: codeharbor_check[:task_found], + task_found: codeharbor_check[:uuid_found], update_right: codeharbor_check[:update_right], error: codeharbor_check[:error], exported: false, @@ -159,10 +159,10 @@ raise: false uuid = params[:uuid] exercise = Exercise.find_by(uuid: uuid) - return render json: {exercise_found: false} if exercise.nil? - return render json: {exercise_found: true, update_right: false} unless ExercisePolicy.new(user, exercise).update? + return render json: {uuid_found: false} if exercise.nil? + return render json: {uuid_found: true, update_right: false} unless ExercisePolicy.new(user, exercise).update? - render json: {exercise_found: true, update_right: true} + render json: {uuid_found: true, update_right: true} end def import_exercise diff --git a/app/services/proforma_service/convert_task_to_exercise.rb b/app/services/proforma_service/convert_task_to_exercise.rb index f4ac2e63..35e62180 100644 --- a/app/services/proforma_service/convert_task_to_exercise.rb +++ b/app/services/proforma_service/convert_task_to_exercise.rb @@ -27,14 +27,23 @@ module ProformaService end def files - test_files + task_files.values + model_solution_files + test_files + task_files.values end def test_files @task.tests.map do |test_object| task_files.delete(test_object.files.first.id).tap do |file| file.weight = 1.0 - file.feedback_message = test_object.meta_data[:CodeOcean]&.dig(:'feedback-message') + file.feedback_message = test_object.meta_data[:CodeOcean]&.dig(:'feedback-message').presence || 'Feedback' + file.role = 'teacher_defined_test' + end + end + end + + def model_solution_files + @task.model_solutions.map do |model_solution_object| + task_files.delete(model_solution_object.files.first.id).tap do |file| + file.role = 'reference_implementation' end end end @@ -53,7 +62,7 @@ module ProformaService hidden: file.visible == 'no', name: File.basename(file.filename, '.*'), read_only: file.usage_by_lms != 'edit', - role: file.internal_description, + role: 'regular_file', path: File.dirname(file.filename).in?(['.', '']) ? nil : File.dirname(file.filename) ) if file.binary diff --git a/app/services/service_base.rb b/app/services/service_base.rb index 39010bb7..902944f3 100644 --- a/app/services/service_base.rb +++ b/app/services/service_base.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class ServiceBase - def self.call(*args) - new(*args).execute + def self.call(**args) + new(**args).execute end end From 50b81df74223e73b7b0aa912ec46c35401f09fc8 Mon Sep 17 00:00:00 2001 From: Karol Date: Tue, 23 Aug 2022 21:12:03 +0200 Subject: [PATCH 09/16] proforma upgrade and small fixes --- Gemfile | 2 +- Gemfile.lock | 14 +++++++------- app/controllers/codeharbor_links_controller.rb | 2 +- app/services/exercise_service/check_external.rb | 4 ++-- .../proforma_service/convert_exercise_to_task.rb | 2 +- app/views/exercises/_export_actions.html.slim | 8 ++++---- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Gemfile b/Gemfile index 79c41778..571e9bdd 100644 --- a/Gemfile +++ b/Gemfile @@ -28,7 +28,7 @@ gem 'net-smtp', require: false gem 'nokogiri' gem 'pagedown-bootstrap-rails' gem 'pg' -gem 'proforma', github: 'openHPI/proforma', branch: 'v0.5.2' +gem 'proforma', github: 'openHPI/proforma', tag: 'v0.7.1' gem 'prometheus_exporter' gem 'pry-byebug' gem 'puma' diff --git a/Gemfile.lock b/Gemfile.lock index 8354d194..d24c77ff 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,13 +1,13 @@ GIT remote: https://github.com/openHPI/proforma.git - revision: 243853e66034bc2afbb9c9661475d9718d007304 - branch: v0.5.2 + revision: cf61517a5cd765afb9d0d19ea1c692e18e3131d7 + tag: v0.7.1 specs: - proforma (0.5.2) - activemodel (>= 5.2.3, < 7.2.0) - activesupport (>= 5.2.3, < 7.2.0) - nokogiri (~> 1.13) - rubyzip (~> 2.3) + proforma (0.7.1) + activemodel (>= 5.2.3, < 8.0.0) + activesupport (>= 5.2.3, < 8.0.0) + nokogiri (>= 1.10.2, < 2.0.0) + rubyzip (>= 1.2.2, < 3.0.0) GEM remote: https://rubygems.org/ diff --git a/app/controllers/codeharbor_links_controller.rb b/app/controllers/codeharbor_links_controller.rb index c98b02b1..0daeb4ea 100644 --- a/app/controllers/codeharbor_links_controller.rb +++ b/app/controllers/codeharbor_links_controller.rb @@ -7,7 +7,7 @@ class CodeharborLinksController < ApplicationController def new base_url = CodeOcean::Config.new(:code_ocean).read[:codeharbor][:url] || '' - @codeharbor_link = CodeharborLink.new(push_url: "#{base_url}/import_exercise", + @codeharbor_link = CodeharborLink.new(push_url: "#{base_url}/import_task", check_uuid_url: "#{base_url}/import_uuid_check") authorize! end diff --git a/app/services/exercise_service/check_external.rb b/app/services/exercise_service/check_external.rb index ab7f0db9..04392d4c 100644 --- a/app/services/exercise_service/check_external.rb +++ b/app/services/exercise_service/check_external.rb @@ -14,9 +14,9 @@ module ExerciseService req.headers['Authorization'] = "Bearer #{@codeharbor_link.api_key}" req.body = {uuid: @uuid}.to_json end - response_hash = JSON.parse(response.body, symbolize_names: true).slice(:task_found, :update_right) + response_hash = JSON.parse(response.body, symbolize_names: true).slice(:uuid_found, :update_right) - {error: false, message: message(response_hash[:task_found], response_hash[:update_right])}.merge(response_hash) + {error: false, message: message(response_hash[:uuid_found], response_hash[:update_right])}.merge(response_hash) rescue Faraday::Error, JSON::ParserError {error: true, message: I18n.t('exercises.export_codeharbor.error')} end diff --git a/app/services/proforma_service/convert_exercise_to_task.rb b/app/services/proforma_service/convert_exercise_to_task.rb index 9a8d7f67..7df4becd 100644 --- a/app/services/proforma_service/convert_exercise_to_task.rb +++ b/app/services/proforma_service/convert_exercise_to_task.rb @@ -109,7 +109,7 @@ module ProformaService filename: filename(file), usage_by_lms: file.read_only ? 'display' : 'edit', visible: file.hidden ? 'no' : 'yes', - internal_description: file.role || 'regular_file' + # internal_description: file.role || 'regular_file' ) add_content_to_task_file(file, task_file) task_file diff --git a/app/views/exercises/_export_actions.html.slim b/app/views/exercises/_export_actions.html.slim index f2d0a2f7..72029aee 100644 --- a/app/views/exercises/_export_actions.html.slim +++ b/app/views/exercises/_export_actions.html.slim @@ -1,7 +1,7 @@ - if error - = button_tag type: 'button', class:'btn btn-primary float-end export-button export-retry-button', data: {exercise_id: exercise.id} do - i.fa-solid.fa-arrows-rotate.confirm-icon - = t('exercises.export_codeharbor.buttons.retry') + = button_tag type: 'button', class:'btn btn-primary float-end export-button export-retry-button', data: {exercise_id: exercise.id} do + i.fa-solid.fa-arrows-rotate.confirm-icon + = t('exercises.export_codeharbor.buttons.retry') - else - unless exported - if !task_found || update_right @@ -9,6 +9,6 @@ i.fa-solid.fa-check.confirm-icon = t('exercises.export_codeharbor.buttons.export') -= button_tag type: 'submit', class:'btn btn-secondary float-end export-button', data: {dismiss: 'modal'} do += button_tag type: 'submit', class:'btn btn-secondary float-end export-button', data: {bs_dismiss: 'modal'} do i.fa-solid.fa-xmark.abort-icon = exported ? t('exercises.export_codeharbor.buttons.close') : t('exercises.export_codeharbor.buttons.abort') From 76c9dfa4e5756f5a36fa17832527d34f08b4403e Mon Sep 17 00:00:00 2001 From: Karol Date: Wed, 24 Aug 2022 00:19:53 +0200 Subject: [PATCH 10/16] add more CO specific data to metadata --- .../proforma_service/convert_exercise_to_task.rb | 10 +++++++--- .../proforma_service/convert_task_to_exercise.rb | 10 ++++++++-- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/app/services/proforma_service/convert_exercise_to_task.rb b/app/services/proforma_service/convert_exercise_to_task.rb index 7df4becd..a60cdbf8 100644 --- a/app/services/proforma_service/convert_exercise_to_task.rb +++ b/app/services/proforma_service/convert_exercise_to_task.rb @@ -23,7 +23,6 @@ module ProformaService title: @exercise.title, description: @exercise.description, internal_description: nil, - # ?: @exercise.instructions, store in meta-data # proglang: proglang, where can we get this information? files: task_files, tests: tests, @@ -32,7 +31,12 @@ module ProformaService model_solutions: model_solutions, meta_data: { CodeOcean: { - instructions: @exercise.instructions, + public: @exercise.public, + hide_file_tree: @exercise.hide_file_tree, + allow_file_creation: @exercise.allow_file_creation, + allow_auto_completion: @exercise.allow_auto_completion, + expected_difficulty: @exercise.expected_difficulty, + execution_environment_id: @exercise.execution_environment_id, }, }, }.compact @@ -78,8 +82,8 @@ module ProformaService def test_meta_data(file) { CodeOcean: { - 'entry-point': file.filepath, 'feedback-message': file.feedback_message, + weight: file.weight, }, } end diff --git a/app/services/proforma_service/convert_task_to_exercise.rb b/app/services/proforma_service/convert_task_to_exercise.rb index 35e62180..bf027e00 100644 --- a/app/services/proforma_service/convert_task_to_exercise.rb +++ b/app/services/proforma_service/convert_task_to_exercise.rb @@ -21,7 +21,13 @@ module ProformaService user: @user, title: @task.title, description: @task.description, - instructions: @task.meta_data&.dig(:CodeOcean)&.dig(:instructions), + public: @task.meta_data[:CodeOcean]&.dig(:public) == 'true', + hide_file_tree: @task.meta_data[:CodeOcean]&.dig(:hide_file_tree) == 'true', + allow_file_creation: @task.meta_data[:CodeOcean]&.dig(:allow_file_creation) == 'true', + allow_auto_completion: @task.meta_data[:CodeOcean]&.dig(:allow_auto_completion) == 'true', + expected_difficulty: @task.meta_data[:CodeOcean]&.dig(:expected_difficulty), + execution_environment_id: @task.meta_data[:CodeOcean]&.dig(:execution_environment_id), + files: files ) end @@ -33,7 +39,7 @@ module ProformaService def test_files @task.tests.map do |test_object| task_files.delete(test_object.files.first.id).tap do |file| - file.weight = 1.0 + file.weight = test_object.meta_data[:CodeOcean]&.dig(:weight) || 1.0 file.feedback_message = test_object.meta_data[:CodeOcean]&.dig(:'feedback-message').presence || 'Feedback' file.role = 'teacher_defined_test' end From 5ace779d0cc5ccf4496b1d34d2d7200390ddb00e Mon Sep 17 00:00:00 2001 From: Karol Date: Wed, 31 Aug 2022 20:51:58 +0200 Subject: [PATCH 11/16] fix and add specs --- app/controllers/exercises_controller.rb | 2 +- .../convert_exercise_to_task.rb | 26 ++++++++---- .../convert_task_to_exercise.rb | 17 +++++--- app/views/exercises/_export_actions.html.slim | 2 +- spec/controllers/exercises_controller_spec.rb | 12 +++--- .../exercise_service/check_external_spec.rb | 16 +++---- .../convert_exercise_to_task_spec.rb | 34 ++++++++++++--- .../convert_task_to_exercise_spec.rb | 42 ++++++++++++++----- 8 files changed, 104 insertions(+), 47 deletions(-) diff --git a/app/controllers/exercises_controller.rb b/app/controllers/exercises_controller.rb index d2595ad6..fafad3c2 100644 --- a/app/controllers/exercises_controller.rb +++ b/app/controllers/exercises_controller.rb @@ -119,7 +119,7 @@ class ExercisesController < ApplicationController partial: 'export_actions', locals: { exercise: @exercise, - task_found: codeharbor_check[:uuid_found], + uuid_found: codeharbor_check[:uuid_found], update_right: codeharbor_check[:update_right], error: codeharbor_check[:error], exported: false, diff --git a/app/services/proforma_service/convert_exercise_to_task.rb b/app/services/proforma_service/convert_exercise_to_task.rb index a60cdbf8..fdc3ff6d 100644 --- a/app/services/proforma_service/convert_exercise_to_task.rb +++ b/app/services/proforma_service/convert_exercise_to_task.rb @@ -37,6 +37,7 @@ module ProformaService allow_auto_completion: @exercise.allow_auto_completion, expected_difficulty: @exercise.expected_difficulty, execution_environment_id: @exercise.execution_environment_id, + files: task_files_meta_data, }, }, }.compact @@ -92,17 +93,26 @@ module ProformaService [ task_file(file).tap do |t_file| t_file.used_by_grader = true - t_file.internal_description = 'teacher_defined_test' end, ] end - def task_files - @exercise.files - .filter do |file| + def exercise_files + @exercise.files.filter do |file| !file.role.in? %w[reference_implementation teacher_defined_test teacher_defined_linter] - end.map do |file| + end + end + + def task_files_meta_data + exercise_files.to_h do |file| + # added CO- to id, otherwise the key would have CodeOcean as a prefix after export and import (cause unknown) + ["CO-#{file.id}", {role: file.role}] + end + end + + def task_files + exercise_files.map do |file| task_file(file) end end @@ -112,8 +122,7 @@ module ProformaService id: file.id, filename: filename(file), usage_by_lms: file.read_only ? 'display' : 'edit', - visible: file.hidden ? 'no' : 'yes', - # internal_description: file.role || 'regular_file' + visible: file.hidden ? 'no' : 'yes' ) add_content_to_task_file(file, task_file) task_file @@ -121,8 +130,7 @@ module ProformaService def filename(file) if file.path.present? && file.path != '.' - ::File.join(file.path, - file.name_with_extension) + ::File.join(file.path, file.name_with_extension) else file.name_with_extension end diff --git a/app/services/proforma_service/convert_task_to_exercise.rb b/app/services/proforma_service/convert_task_to_exercise.rb index bf027e00..5c9bee78 100644 --- a/app/services/proforma_service/convert_task_to_exercise.rb +++ b/app/services/proforma_service/convert_task_to_exercise.rb @@ -21,10 +21,10 @@ module ProformaService user: @user, title: @task.title, description: @task.description, - public: @task.meta_data[:CodeOcean]&.dig(:public) == 'true', - hide_file_tree: @task.meta_data[:CodeOcean]&.dig(:hide_file_tree) == 'true', - allow_file_creation: @task.meta_data[:CodeOcean]&.dig(:allow_file_creation) == 'true', - allow_auto_completion: @task.meta_data[:CodeOcean]&.dig(:allow_auto_completion) == 'true', + public: string_to_bool(@task.meta_data[:CodeOcean]&.dig(:public)), + hide_file_tree: string_to_bool(@task.meta_data[:CodeOcean]&.dig(:hide_file_tree)), + allow_file_creation: string_to_bool(@task.meta_data[:CodeOcean]&.dig(:allow_file_creation)), + allow_auto_completion: string_to_bool(@task.meta_data[:CodeOcean]&.dig(:allow_auto_completion)), expected_difficulty: @task.meta_data[:CodeOcean]&.dig(:expected_difficulty), execution_environment_id: @task.meta_data[:CodeOcean]&.dig(:execution_environment_id), @@ -32,6 +32,13 @@ module ProformaService ) end + def string_to_bool(str) + return true if str == 'true' + return false if str == 'false' + + nil + end + def files model_solution_files + test_files + task_files.values end @@ -68,7 +75,7 @@ module ProformaService hidden: file.visible == 'no', name: File.basename(file.filename, '.*'), read_only: file.usage_by_lms != 'edit', - role: 'regular_file', + role: @task.meta_data[:CodeOcean]&.dig(:files)&.dig("CO-#{file.id}".to_sym)&.dig(:role) || 'regular_file', path: File.dirname(file.filename).in?(['.', '']) ? nil : File.dirname(file.filename) ) if file.binary diff --git a/app/views/exercises/_export_actions.html.slim b/app/views/exercises/_export_actions.html.slim index 72029aee..73993cb9 100644 --- a/app/views/exercises/_export_actions.html.slim +++ b/app/views/exercises/_export_actions.html.slim @@ -4,7 +4,7 @@ = t('exercises.export_codeharbor.buttons.retry') - else - unless exported - - if !task_found || update_right + - if !uuid_found || update_right = button_tag type: 'button', class:'btn btn-primary float-end export-action export-button', data: {exercise_id: exercise.id} do i.fa-solid.fa-check.confirm-icon = t('exercises.export_codeharbor.buttons.export') diff --git a/spec/controllers/exercises_controller_spec.rb b/spec/controllers/exercises_controller_spec.rb index 9ca7aa9f..d0e9768a 100644 --- a/spec/controllers/exercises_controller_spec.rb +++ b/spec/controllers/exercises_controller_spec.rb @@ -387,7 +387,7 @@ describe ExercisesController do let(:post_request) { post :export_external_check, params: {id: exercise.id} } let!(:codeharbor_link) { create(:codeharbor_link, user: user) } - let(:external_check_hash) { {message: message, task_found: true, update_right: update_right, error: error} } + let(:external_check_hash) { {message: message, uuid_found: true, update_right: update_right, error: error} } let(:message) { 'message' } let(:update_right) { true } let(:error) { nil } @@ -436,7 +436,7 @@ describe ExercisesController do end end - describe '#export_external_confirm' do + describe 'POST #export_external_confirm' do render_views let!(:codeharbor_link) { create(:codeharbor_link, user: user) } @@ -473,7 +473,7 @@ describe ExercisesController do end end - describe '#import_uuid_check' do + describe 'POST #import_uuid_check' do let(:exercise) { create(:dummy, uuid: SecureRandom.uuid) } let!(:codeharbor_link) { create(:codeharbor_link, user: user) } let(:uuid) { exercise.reload.uuid } @@ -486,7 +486,7 @@ describe ExercisesController do post_request expect(response).to have_http_status(:success) - expect(JSON.parse(response.body).symbolize_keys[:exercise_found]).to be true + expect(JSON.parse(response.body).symbolize_keys[:uuid_found]).to be true expect(JSON.parse(response.body).symbolize_keys[:update_right]).to be true end @@ -506,7 +506,7 @@ describe ExercisesController do post_request expect(response).to have_http_status(:success) - expect(JSON.parse(response.body).symbolize_keys[:exercise_found]).to be true + expect(JSON.parse(response.body).symbolize_keys[:uuid_found]).to be true expect(JSON.parse(response.body).symbolize_keys[:update_right]).to be false end end @@ -518,7 +518,7 @@ describe ExercisesController do post_request expect(response).to have_http_status(:success) - expect(JSON.parse(response.body).symbolize_keys[:exercise_found]).to be false + expect(JSON.parse(response.body).symbolize_keys[:uuid_found]).to be false end end end diff --git a/spec/services/exercise_service/check_external_spec.rb b/spec/services/exercise_service/check_external_spec.rb index 3dffeae4..4244e85b 100644 --- a/spec/services/exercise_service/check_external_spec.rb +++ b/spec/services/exercise_service/check_external_spec.rb @@ -42,25 +42,25 @@ describe ExerciseService::CheckExternal do end context 'when response contains a JSON with expected keys' do - let(:response) { {task_found: true, update_right: true}.to_json } + let(:response) { {uuid_found: true, update_right: true}.to_json } it 'returns the correct hash' do - expect(check_external_service).to eql(error: false, message: I18n.t('exercises.export_codeharbor.check.task_found'), task_found: true, update_right: true) + expect(check_external_service).to eql(error: false, message: I18n.t('exercises.export_codeharbor.check.task_found'), uuid_found: true, update_right: true) end - context 'with task_found: false and no update_right' do - let(:response) { {task_found: false}.to_json } + context 'with uuid_found: false and no update_right' do + let(:response) { {uuid_found: false}.to_json } it 'returns the correct hash' do - expect(check_external_service).to eql(error: false, message: I18n.t('exercises.export_codeharbor.check.no_task'), task_found: false) + expect(check_external_service).to eql(error: false, message: I18n.t('exercises.export_codeharbor.check.no_task'), uuid_found: false) end end - context 'with task_found: true and update_right: false' do - let(:response) { {task_found: true, update_right: false}.to_json } + context 'with uuid_found: true and update_right: false' do + let(:response) { {uuid_found: true, update_right: false}.to_json } it 'returns the correct hash' do - expect(check_external_service).to eql(error: false, message: I18n.t('exercises.export_codeharbor.check.task_found_no_right'), task_found: true, update_right: false) + expect(check_external_service).to eql(error: false, message: I18n.t('exercises.export_codeharbor.check.task_found_no_right'), uuid_found: true, update_right: false) end end end diff --git a/spec/services/proforma_service/convert_exercise_to_task_spec.rb b/spec/services/proforma_service/convert_exercise_to_task_spec.rb index 0fffcf59..f43d2f41 100644 --- a/spec/services/proforma_service/convert_exercise_to_task_spec.rb +++ b/spec/services/proforma_service/convert_exercise_to_task_spec.rb @@ -39,7 +39,13 @@ RSpec.describe ProformaService::ConvertExerciseToTask do language: described_class::DEFAULT_LANGUAGE, meta_data: { CodeOcean: { - instructions: exercise.instructions, + allow_auto_completion: exercise.allow_auto_completion, + allow_file_creation: exercise.allow_file_creation, + execution_environment_id: exercise.execution_environment_id, + expected_difficulty: exercise.expected_difficulty, + hide_file_tree: exercise.hide_file_tree, + public: exercise.public, + files: {}, }, }, # parent_uuid: exercise.clone_relations.first&.origin&.uuid, @@ -62,7 +68,15 @@ RSpec.describe ProformaService::ConvertExerciseToTask do usage_by_lms: 'edit', visible: 'yes', binary: false, - internal_description: 'main_file' + internal_description: nil + ) + end + + it 'adds the file\'s role to the file hash in task-meta_data' do + expect(task).to have_attributes( + meta_data: { + CodeOcean: a_hash_including(files: {"CO-#{file.id}" => {role: 'main_file'}}), + } ) end end @@ -82,7 +96,15 @@ RSpec.describe ProformaService::ConvertExerciseToTask do usage_by_lms: 'display', visible: 'no', binary: false, - internal_description: 'regular_file' + internal_description: nil + ) + end + + it 'adds the file\'s role to the file hash in task-meta_data' do + expect(task).to have_attributes( + meta_data: { + CodeOcean: a_hash_including(files: {"CO-#{file.id}" => {role: 'regular_file'}}), + } ) end @@ -139,7 +161,7 @@ RSpec.describe ProformaService::ConvertExerciseToTask do usage_by_lms: 'display', visible: 'yes', binary: false, - internal_description: 'reference_implementation' + internal_description: nil ) end end @@ -168,8 +190,8 @@ RSpec.describe ProformaService::ConvertExerciseToTask do files: have(1).item, meta_data: { CodeOcean: { - 'entry-point': test_file.filepath, 'feedback-message': 'feedback_message', + weight: test_file.weight, }, } ) @@ -183,7 +205,7 @@ RSpec.describe ProformaService::ConvertExerciseToTask do used_by_grader: true, visible: 'no', binary: false, - internal_description: 'teacher_defined_test' + internal_description: nil ) 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 29c5f431..071c3472 100644 --- a/spec/services/proforma_service/convert_task_to_exercise_spec.rb +++ b/spec/services/proforma_service/convert_task_to_exercise_spec.rb @@ -38,32 +38,54 @@ describe ProformaService::ConvertTaskToExercise do uuid: 'uuid', parent_uuid: 'parent_uuid', language: 'language', - meta_data: { - CodeOcean: { - instructions: 'instructions', - }, - }, + meta_data: meta_data, model_solutions: model_solutions, files: files, tests: tests ) end let(:user) { create(:teacher) } + let(:files) { [] } let(:tests) { [] } let(:model_solutions) { [] } let(:exercise) { nil } + let(:meta_data) do + { + CodeOcean: { + public: public, + hide_file_tree: hide_file_tree, + allow_file_creation: allow_file_creation, + allow_auto_completion: allow_auto_completion, + expected_difficulty: expected_difficulty, + execution_environment_id: execution_environment.id, + files: files_meta_data, + }, + } + end + let(:public) { 'true' } + let(:hide_file_tree) { 'true' } + let(:allow_file_creation) { 'true' } + let(:allow_auto_completion) { 'true' } + let(:expected_difficulty) { 7 } + let!(:execution_environment) { create(:java) } + let(:files_meta_data) { {} } + it 'creates an exercise with the correct attributes' do expect(convert_to_exercise_service).to have_attributes( title: 'title', description: 'description', - instructions: 'instructions', - execution_environment: be_blank, uuid: be_blank, unpublished: true, user: user, - files: be_empty + files: be_empty, + public: true, + hide_file_tree: true, + allow_file_creation: true, + allow_auto_completion: true, + expected_difficulty: 7, + execution_environment_id: execution_environment.id ) end @@ -311,8 +333,7 @@ describe ProformaService::ConvertTaskToExercise do create( :files, title: 'exercise-title', - description: 'exercise-description', - instructions: 'exercise-instruction' + description: 'exercise-description' ) end @@ -324,7 +345,6 @@ describe ProformaService::ConvertTaskToExercise do id: exercise.id, title: task.title, description: task.description, - instructions: task.meta_data[:CodeOcean][:instructions], execution_environment: exercise.execution_environment, uuid: exercise.uuid, user: exercise.user, From 5ec2c7f5bc644babf986fb2d5d66bffa4c5a1145 Mon Sep 17 00:00:00 2001 From: Karol Date: Tue, 13 Sep 2022 22:47:12 +0200 Subject: [PATCH 12/16] refactor role finding logic for convert_task_to_exercise add specs --- .../convert_exercise_to_task.rb | 4 +-- .../convert_task_to_exercise.rb | 8 +++--- .../convert_task_to_exercise_spec.rb | 28 +++++++++++++------ 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/app/services/proforma_service/convert_exercise_to_task.rb b/app/services/proforma_service/convert_exercise_to_task.rb index fdc3ff6d..937f1898 100644 --- a/app/services/proforma_service/convert_exercise_to_task.rb +++ b/app/services/proforma_service/convert_exercise_to_task.rb @@ -68,9 +68,7 @@ module ProformaService end def tests - @exercise.files.filter do |file| - file.role == 'teacher_defined_test' || file.role == 'teacher_defined_linter' - end.map do |file| + @exercise.files.filter(&:teacher_defined_assessment?).map do |file| Proforma::Test.new( id: file.id, title: file.name, diff --git a/app/services/proforma_service/convert_task_to_exercise.rb b/app/services/proforma_service/convert_task_to_exercise.rb index 5c9bee78..7ecf7881 100644 --- a/app/services/proforma_service/convert_task_to_exercise.rb +++ b/app/services/proforma_service/convert_task_to_exercise.rb @@ -40,7 +40,7 @@ module ProformaService end def files - model_solution_files + test_files + task_files.values + model_solution_files + test_files + task_files.values.tap {|array| array.each {|file| file.role ||= 'regular_file' } } end def test_files @@ -48,7 +48,7 @@ module ProformaService task_files.delete(test_object.files.first.id).tap do |file| file.weight = test_object.meta_data[:CodeOcean]&.dig(:weight) || 1.0 file.feedback_message = test_object.meta_data[:CodeOcean]&.dig(:'feedback-message').presence || 'Feedback' - file.role = 'teacher_defined_test' + file.role ||= 'teacher_defined_test' end end end @@ -56,7 +56,7 @@ module ProformaService def model_solution_files @task.model_solutions.map do |model_solution_object| task_files.delete(model_solution_object.files.first.id).tap do |file| - file.role = 'reference_implementation' + file.role ||= 'reference_implementation' end end end @@ -75,7 +75,7 @@ module ProformaService hidden: file.visible == 'no', name: File.basename(file.filename, '.*'), read_only: file.usage_by_lms != 'edit', - role: @task.meta_data[:CodeOcean]&.dig(:files)&.dig("CO-#{file.id}".to_sym)&.dig(:role) || 'regular_file', + role: @task.meta_data[:CodeOcean]&.dig(:files)&.dig("CO-#{file.id}".to_sym)&.dig(:role), path: File.dirname(file.filename).in?(['.', '']) ? nil : File.dirname(file.filename) ) if file.binary 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 071c3472..bf19bb3e 100644 --- a/spec/services/proforma_service/convert_task_to_exercise_spec.rb +++ b/spec/services/proforma_service/convert_task_to_exercise_spec.rb @@ -3,8 +3,6 @@ require 'rails_helper' describe ProformaService::ConvertTaskToExercise do - # TODO: Add teacher_defined_linter for tests - describe '.new' do subject(:convert_to_exercise_service) { described_class.new(task: task, user: user, exercise: exercise) } @@ -100,7 +98,6 @@ describe ProformaService::ConvertTaskToExercise do visible: 'yes', usage_by_lms: usage_by_lms, binary: binary, - internal_description: 'regular_file', mimetype: mimetype ) end @@ -127,6 +124,14 @@ describe ProformaService::ConvertTaskToExercise do expect { convert_to_exercise_service.save! }.to change(Exercise, :count).by(1) end + context 'when file is a main_file' do + let(:files_meta_data) { {"CO-#{file.id}".to_sym => {role: 'main_file'}} } + + it 'creates an exercise with a file that has the correct attributes' do + expect(convert_to_exercise_service.files.first).to have_attributes(role: 'main_file') + end + end + context 'when path is folder/' do let(:path) { 'folder/' } @@ -176,7 +181,7 @@ describe ProformaService::ConvertTaskToExercise do end end - context 'when file has an unkown file_type' do + context 'when file has an unknown file_type' do let(:filename) { 'unknown_file_type.asdf' } it 'creates a new Exercise on save' do @@ -206,8 +211,7 @@ describe ProformaService::ConvertTaskToExercise do used_by_grader: 'used_by_grader', visible: 'yes', usage_by_lms: 'display', - binary: false, - internal_description: 'reference_implementation' + binary: false ) end @@ -294,6 +298,14 @@ describe ProformaService::ConvertTaskToExercise do ) end + context 'when test file is a teacher_defined_linter' do + let(:files_meta_data) { {"CO-#{test_file.id}".to_sym => {role: 'teacher_defined_linter'}} } + + it 'creates an exercise with a test' do + expect(convert_to_exercise_service.files.select {|file| file.role == 'teacher_defined_linter' }).to have(1).item + end + end + context 'when task has multiple tests' do let(:tests) { [test, test2] } let(:test2) do @@ -366,8 +378,7 @@ describe ProformaService::ConvertTaskToExercise do used_by_grader: 'used_by_grader', visible: 'yes', usage_by_lms: 'display', - binary: false, - internal_description: 'regular_file' + binary: false ) end let(:tests) { [test] } @@ -376,7 +387,6 @@ describe ProformaService::ConvertTaskToExercise do id: 'test-id', title: 'title', description: 'description', - internal_description: 'regular_file', test_type: 'test_type', files: test_files, meta_data: { From f5758ecb5eb14826d99923fafa31f43c8801c7fa Mon Sep 17 00:00:00 2001 From: Sebastian Serth Date: Wed, 21 Sep 2022 09:56:07 +0200 Subject: [PATCH 13/16] Capitalize app names in locales --- config/locales/de.yml | 2 +- config/locales/en.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/locales/de.yml b/config/locales/de.yml index 7b059c17..81aa6db3 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -391,7 +391,7 @@ de: abort: Abbrechen check: no_task: Auf CodeHarbor wurde keine entsprechende Aufgabe gefunden. Mit dem Export der Aufgabe wird eine neue auf CodeHarbor angelegt, die mit dieser verbunden ist. Anschließend können Veränderungen an der Aufgabe von beiden Systemen aus jeweils in das andere Übertragen werden. - task_found: Auf CodeHarbor wurde eine entsprechende Aufgabe gefunden. Mit dem Export der Aufgabe werden alle Veränderungen, die auf Codeocean vorgenommen wurden, exportiert und die Aufgabe auf CodeHarbor überschrieben. + task_found: Auf CodeHarbor wurde eine entsprechende Aufgabe gefunden. Mit dem Export der Aufgabe werden alle Veränderungen, die auf CodeOcean vorgenommen wurden, exportiert und die Aufgabe auf CodeHarbor überschrieben. task_found_no_right: Auf CodeHarbor wurde eine entsprechende Aufgabe gefunden, Sie haben aber keine Rechte sie zu bearbeiten. Bitte wenden Sie sich an einen Admin, wenn Sie denken, dass Sie die Rechte dazu besitzen sollten. file_form: hints: diff --git a/config/locales/en.yml b/config/locales/en.yml index 6c7e7612..fee3d1cb 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -390,8 +390,8 @@ en: close: Close abort: Abort check: - no_task: No corresponding task found on CodeHarbor. Pushing this exercise will create a new task on CodeHarbor, which will be linked to this one on Codeocean. Any changes to either one can be pushed to the respective other platform. - task_found: 'A corresponding task has been found on CodeHarbor. You can export the exercise to transfer all changes made on Codeocean to Codeharbor. Careful: This will overwrite all potential changes made on Codeharbor.' + no_task: No corresponding task found on CodeHarbor. Pushing this exercise will create a new task on CodeHarbor, which will be linked to this one on CodeOcean. Any changes to either one can be pushed to the respective other platform. + task_found: 'A corresponding task has been found on CodeHarbor. You can export the exercise to transfer all changes made on CodeOcean to CodeHarbor. Careful: This will overwrite all potential changes made on CodeHarbor.' task_found_no_right: A corresponding task has been found on CodeHarbor, but you don't have the rights to edit it. Please contact an Admin if you think you should be able to edit the task on CodeHarbor. file_form: hints: From 77999f75df4fe0e8aca12475e95a102c3396128e Mon Sep 17 00:00:00 2001 From: Karol Date: Sat, 24 Sep 2022 15:24:12 +0200 Subject: [PATCH 14/16] set proglang based on exenv try to guess exenv based on proglang --- .../proforma_service/convert_exercise_to_task.rb | 8 +++++++- .../proforma_service/convert_task_to_exercise.rb | 16 +++++++++++++++- .../convert_exercise_to_task_spec.rb | 14 ++++++++------ .../convert_task_to_exercise_spec.rb | 14 ++++++++++++-- 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/app/services/proforma_service/convert_exercise_to_task.rb b/app/services/proforma_service/convert_exercise_to_task.rb index 937f1898..0acb062a 100644 --- a/app/services/proforma_service/convert_exercise_to_task.rb +++ b/app/services/proforma_service/convert_exercise_to_task.rb @@ -23,7 +23,7 @@ module ProformaService title: @exercise.title, description: @exercise.description, internal_description: nil, - # proglang: proglang, where can we get this information? + proglang: proglang, files: task_files, tests: tests, uuid: uuid, @@ -44,6 +44,12 @@ module ProformaService ) end + def proglang + regex = %r{^openhpi/co_execenv_(?[^:]*):(?[^-]*)(?>-.*)?$} + match = regex.match @exercise.execution_environment.docker_image + match ? {name: match[:language], version: match[:version]} : nil + end + def uuid @exercise.update(uuid: SecureRandom.uuid) if @exercise.uuid.nil? @exercise.uuid diff --git a/app/services/proforma_service/convert_task_to_exercise.rb b/app/services/proforma_service/convert_task_to_exercise.rb index 7ecf7881..a0cbeaf7 100644 --- a/app/services/proforma_service/convert_task_to_exercise.rb +++ b/app/services/proforma_service/convert_task_to_exercise.rb @@ -26,12 +26,26 @@ module ProformaService allow_file_creation: string_to_bool(@task.meta_data[:CodeOcean]&.dig(:allow_file_creation)), allow_auto_completion: string_to_bool(@task.meta_data[:CodeOcean]&.dig(:allow_auto_completion)), expected_difficulty: @task.meta_data[:CodeOcean]&.dig(:expected_difficulty), - execution_environment_id: @task.meta_data[:CodeOcean]&.dig(:execution_environment_id), + execution_environment_id: execution_environment_id, files: files ) end + def execution_environment_id + from_meta_data = @task.meta_data[:CodeOcean]&.dig(:execution_environment_id) + return from_meta_data if from_meta_data + return nil unless @task.proglang + + ex_envs_with_name_and_version = ExecutionEnvironment.where('docker_image ilike ?', "%#{@task.proglang[:name]}%#{@task.proglang[:version]}%") + return ex_envs_with_name_and_version.first.id if ex_envs_with_name_and_version.any? + + ex_envs_with_name = ExecutionEnvironment.where('docker_image like ?', "%#{@task.proglang[:name]}%") + return ex_envs_with_name.first.id if ex_envs_with_name.any? + + nil + end + def string_to_bool(str) return true if str == 'true' return false if str == 'false' diff --git a/spec/services/proforma_service/convert_exercise_to_task_spec.rb b/spec/services/proforma_service/convert_exercise_to_task_spec.rb index f43d2f41..bf9f5b40 100644 --- a/spec/services/proforma_service/convert_exercise_to_task_spec.rb +++ b/spec/services/proforma_service/convert_exercise_to_task_spec.rb @@ -19,22 +19,19 @@ RSpec.describe ProformaService::ConvertExerciseToTask do let(:convert_to_task) { described_class.new(exercise: exercise) } let(:exercise) do create(:dummy, + execution_environment: execution_environment, instructions: 'instruction', uuid: SecureRandom.uuid, files: files + tests) end let(:files) { [] } let(:tests) { [] } + let(:execution_environment) { create(:java) } it 'creates a task with all basic attributes' do expect(task).to have_attributes( title: exercise.title, description: exercise.description, - # internal_description: exercise.instructions, - # proglang: { - # name: exercise.execution_environment.language, - # version: exercise.execution_environment.version - # }, uuid: exercise.uuid, language: described_class::DEFAULT_LANGUAGE, meta_data: { @@ -48,13 +45,18 @@ RSpec.describe ProformaService::ConvertExerciseToTask do files: {}, }, }, - # parent_uuid: exercise.clone_relations.first&.origin&.uuid, files: [], tests: [], model_solutions: [] ) end + context 'when exercise has execution_environment with correct docker-image name' do + it 'creates a task with the correct proglang attribute' do + expect(task).to have_attributes(proglang: {name: 'java', version: '8'}) + end + end + context 'when exercise has a mainfile' do let(:files) { [file] } let(:file) { build(:file) } 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 bf19bb3e..22b649da 100644 --- a/spec/services/proforma_service/convert_task_to_exercise_spec.rb +++ b/spec/services/proforma_service/convert_task_to_exercise_spec.rb @@ -32,7 +32,7 @@ describe ProformaService::ConvertTaskToExercise do Proforma::Task.new( title: 'title', description: 'description', - # proglang: {name: 'proglang-name', version: 'proglang-version'}, + proglang: {name: 'python', version: '3.4'}, uuid: 'uuid', parent_uuid: 'parent_uuid', language: 'language', @@ -57,7 +57,7 @@ describe ProformaService::ConvertTaskToExercise do allow_file_creation: allow_file_creation, allow_auto_completion: allow_auto_completion, expected_difficulty: expected_difficulty, - execution_environment_id: execution_environment.id, + execution_environment_id: execution_environment&.id, files: files_meta_data, }, } @@ -87,6 +87,16 @@ describe ProformaService::ConvertTaskToExercise do ) end + context 'when execution environment is not set in meta_data' do + let(:execution_environment) { nil } + + before { create(:python) } + + it 'sets the execution_environment based on proglang name and value' do + expect(convert_to_exercise_service).to have_attributes(execution_environment: have_attributes(name: 'Python 3.4')) + end + end + context 'when task has a file' do let(:files) { [file] } let(:file) do From b8662213534279e62206d0b9848d72083b1475d2 Mon Sep 17 00:00:00 2001 From: Karol Date: Sat, 24 Sep 2022 21:08:18 +0200 Subject: [PATCH 15/16] rename route --- app/controllers/exercises_controller.rb | 8 ++++---- app/services/proforma_service/convert_task_to_exercise.rb | 4 ++-- config/routes.rb | 2 +- spec/controllers/exercises_controller_spec.rb | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/controllers/exercises_controller.rb b/app/controllers/exercises_controller.rb index 5d5eec43..4f4c1679 100644 --- a/app/controllers/exercises_controller.rb +++ b/app/controllers/exercises_controller.rb @@ -18,9 +18,9 @@ class ExercisesController < ApplicationController before_action :set_course_token, only: [:implement] before_action :set_available_tips, only: %i[implement show new edit] - skip_before_action :verify_authenticity_token, only: %i[import_exercise import_uuid_check] - skip_after_action :verify_authorized, only: %i[import_exercise import_uuid_check] - skip_after_action :verify_policy_scoped, only: %i[import_exercise import_uuid_check], raise: false + skip_before_action :verify_authenticity_token, only: %i[import_task import_uuid_check] + skip_after_action :verify_authorized, only: %i[import_task import_uuid_check] + skip_after_action :verify_policy_scoped, only: %i[import_task import_uuid_check], raise: false rescue_from Pundit::NotAuthorizedError, with: :not_authorized_for_exercise @@ -169,7 +169,7 @@ class ExercisesController < ApplicationController render json: {uuid_found: true, update_right: true} end - def import_exercise + def import_task tempfile = Tempfile.new('codeharbor_import.zip') tempfile.write request.body.read.force_encoding('UTF-8') tempfile.rewind diff --git a/app/services/proforma_service/convert_task_to_exercise.rb b/app/services/proforma_service/convert_task_to_exercise.rb index a0cbeaf7..31b4f2c4 100644 --- a/app/services/proforma_service/convert_task_to_exercise.rb +++ b/app/services/proforma_service/convert_task_to_exercise.rb @@ -10,13 +10,13 @@ module ProformaService end def execute - import_exercise + import_task @exercise end private - def import_exercise + def import_task @exercise.assign_attributes( user: @user, title: @task.title, diff --git a/config/routes.rb b/config/routes.rb index dc276e13..9e3dc3b8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -73,7 +73,7 @@ Rails.application.routes.draw do post :sync_all_to_runner_management, on: :collection end - post '/import_exercise' => 'exercises#import_exercise' + post '/import_task' => 'exercises#import_task' post '/import_uuid_check' => 'exercises#import_uuid_check' resources :exercises do diff --git a/spec/controllers/exercises_controller_spec.rb b/spec/controllers/exercises_controller_spec.rb index 05631045..b6c38477 100644 --- a/spec/controllers/exercises_controller_spec.rb +++ b/spec/controllers/exercises_controller_spec.rb @@ -539,10 +539,10 @@ describe ExercisesController do end end - describe 'POST #import_exercise' do + describe 'POST #import_task' do let(:codeharbor_link) { create(:codeharbor_link, user: user) } let!(:imported_exercise) { create(:fibonacci) } - let(:post_request) { post :import_exercise, body: zip_file_content } + let(:post_request) { post :import_task, body: zip_file_content } let(:zip_file_content) { 'zipped task xml' } let(:headers) { {'Authorization' => "Bearer #{codeharbor_link.api_key}"} } From d6cd06550dbb9bce55d7735696806db4ad580a62 Mon Sep 17 00:00:00 2001 From: Karol Date: Mon, 26 Sep 2022 20:46:40 +0200 Subject: [PATCH 16/16] add default values to meta_data fix specs --- .../convert_task_to_exercise.rb | 10 +-- .../convert_task_to_exercise_spec.rb | 81 ++++++++++++++----- spec/services/proforma_service/import_spec.rb | 4 + 3 files changed, 68 insertions(+), 27 deletions(-) diff --git a/app/services/proforma_service/convert_task_to_exercise.rb b/app/services/proforma_service/convert_task_to_exercise.rb index 31b4f2c4..c32e5321 100644 --- a/app/services/proforma_service/convert_task_to_exercise.rb +++ b/app/services/proforma_service/convert_task_to_exercise.rb @@ -21,11 +21,11 @@ module ProformaService user: @user, title: @task.title, description: @task.description, - public: string_to_bool(@task.meta_data[:CodeOcean]&.dig(:public)), - hide_file_tree: string_to_bool(@task.meta_data[:CodeOcean]&.dig(:hide_file_tree)), - allow_file_creation: string_to_bool(@task.meta_data[:CodeOcean]&.dig(:allow_file_creation)), - allow_auto_completion: string_to_bool(@task.meta_data[:CodeOcean]&.dig(:allow_auto_completion)), - expected_difficulty: @task.meta_data[:CodeOcean]&.dig(:expected_difficulty), + public: string_to_bool(@task.meta_data[:CodeOcean]&.dig(:public)) || false, + hide_file_tree: string_to_bool(@task.meta_data[:CodeOcean]&.dig(:hide_file_tree)) || false, + allow_file_creation: string_to_bool(@task.meta_data[:CodeOcean]&.dig(:allow_file_creation)) || false, + allow_auto_completion: string_to_bool(@task.meta_data[:CodeOcean]&.dig(:allow_auto_completion)) || false, + expected_difficulty: @task.meta_data[:CodeOcean]&.dig(:expected_difficulty) || 1, execution_environment_id: execution_environment_id, files: files 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 22b649da..f8a06cbc 100644 --- a/spec/services/proforma_service/convert_task_to_exercise_spec.rb +++ b/spec/services/proforma_service/convert_task_to_exercise_spec.rb @@ -49,26 +49,13 @@ describe ProformaService::ConvertTaskToExercise do let(:model_solutions) { [] } let(:exercise) { nil } - let(:meta_data) do - { - CodeOcean: { - public: public, - hide_file_tree: hide_file_tree, - allow_file_creation: allow_file_creation, - allow_auto_completion: allow_auto_completion, - expected_difficulty: expected_difficulty, - execution_environment_id: execution_environment&.id, - files: files_meta_data, - }, - } - end + let(:meta_data) { {} } let(:public) { 'true' } let(:hide_file_tree) { 'true' } let(:allow_file_creation) { 'true' } let(:allow_auto_completion) { 'true' } let(:expected_difficulty) { 7 } let!(:execution_environment) { create(:java) } - let(:files_meta_data) { {} } it 'creates an exercise with the correct attributes' do expect(convert_to_exercise_service).to have_attributes( @@ -78,15 +65,51 @@ describe ProformaService::ConvertTaskToExercise do unpublished: true, user: user, files: be_empty, - public: true, - hide_file_tree: true, - allow_file_creation: true, - allow_auto_completion: true, - expected_difficulty: 7, - execution_environment_id: execution_environment.id + public: false, + hide_file_tree: false, + allow_file_creation: false, + allow_auto_completion: false, + expected_difficulty: 1, + execution_environment_id: nil ) end + it { is_expected.to be_valid } + + context 'when meta_data is set' do + let(:meta_data) do + { + CodeOcean: { + public: public, + hide_file_tree: hide_file_tree, + allow_file_creation: allow_file_creation, + allow_auto_completion: allow_auto_completion, + expected_difficulty: expected_difficulty, + execution_environment_id: execution_environment&.id, + files: files_meta_data, + }, + } + end + let(:files_meta_data) { {} } + + it 'creates an exercise with the correct attributes' do + expect(convert_to_exercise_service).to have_attributes( + title: 'title', + description: 'description', + uuid: be_blank, + unpublished: true, + user: user, + files: be_empty, + public: true, + hide_file_tree: true, + allow_file_creation: true, + allow_auto_completion: true, + expected_difficulty: 7, + execution_environment_id: execution_environment.id + ) + end + end + context 'when execution environment is not set in meta_data' do let(:execution_environment) { nil } @@ -135,6 +158,13 @@ describe ProformaService::ConvertTaskToExercise do end context 'when file is a main_file' do + let(:meta_data) do + { + CodeOcean: { + files: files_meta_data, + }, + } + end let(:files_meta_data) { {"CO-#{file.id}".to_sym => {role: 'main_file'}} } it 'creates an exercise with a file that has the correct attributes' do @@ -309,6 +339,13 @@ describe ProformaService::ConvertTaskToExercise do end context 'when test file is a teacher_defined_linter' do + let(:meta_data) do + { + CodeOcean: { + files: files_meta_data, + }, + } + end let(:files_meta_data) { {"CO-#{test_file.id}".to_sym => {role: 'teacher_defined_linter'}} } it 'creates an exercise with a test' do @@ -365,8 +402,8 @@ describe ProformaService::ConvertTaskToExercise do convert_to_exercise_service.save expect(exercise.reload).to have_attributes( id: exercise.id, - title: task.title, - description: task.description, + title: exercise.title, + description: exercise.description, execution_environment: exercise.execution_environment, uuid: exercise.uuid, user: exercise.user, diff --git a/spec/services/proforma_service/import_spec.rb b/spec/services/proforma_service/import_spec.rb index 1f27d731..359c3500 100644 --- a/spec/services/proforma_service/import_spec.rb +++ b/spec/services/proforma_service/import_spec.rb @@ -29,6 +29,10 @@ describe ProformaService::Import do instructions: 'instruction', execution_environment: execution_environment, files: files + tests, + hide_file_tree: true, + allow_file_creation: false, + allow_auto_completion: true, + expected_difficulty: 7, uuid: uuid, user: user) end