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,