From e5d2b3ff280500622423bf5f7e8da818b4bac3d5 Mon Sep 17 00:00:00 2001 From: karol Date: Wed, 19 Mar 2025 14:04:40 +0100 Subject: [PATCH] add direct download of proforma zip --- app/controllers/exercises_controller.rb | 9 +++++- app/policies/exercise_policy.rb | 2 +- app/views/exercises/index.html.slim | 1 + app/views/exercises/show.html.slim | 1 + config/locales/de/exercise.yml | 2 ++ config/locales/en/exercise.yml | 2 ++ config/routes.rb | 1 + spec/controllers/exercises_controller_spec.rb | 30 +++++++++++++++++++ 8 files changed, 46 insertions(+), 2 deletions(-) diff --git a/app/controllers/exercises_controller.rb b/app/controllers/exercises_controller.rb index f8e72be19..6ee49fc8f 100644 --- a/app/controllers/exercises_controller.rb +++ b/app/controllers/exercises_controller.rb @@ -12,7 +12,7 @@ class ExercisesController < ApplicationController before_action :set_exercise_and_authorize, only: MEMBER_ACTIONS + %i[clone implement working_times intervention statistics reload feedback study_group_dashboard export_external_check export_external_confirm - external_user_statistics] + external_user_statistics download_proforma] before_action :collect_set_and_unset_exercise_tags, only: MEMBER_ACTIONS before_action :set_external_user_and_authorize, only: [:external_user_statistics] before_action :set_file_types, only: %i[create edit new update] @@ -181,6 +181,13 @@ def import_task render json: t('exercises.import_codeharbor.import_errors.internal_error'), status: :internal_server_error end + def download_proforma + zip_file = ProformaService::ExportTask.call(exercise: @exercise) + send_data(zip_file.string, type: 'application/zip', filename: "exercise_#{@exercise.id}.zip", disposition: 'attachment') + rescue ProformaXML::PostGenerateValidationError => e + redirect_to :root, danger: JSON.parse(e.message).join('
') + end + def user_from_api_key authorization_header = request.headers['Authorization'] api_key = authorization_header&.split&.second diff --git a/app/policies/exercise_policy.rb b/app/policies/exercise_policy.rb index cd07b5f4d..b64671a59 100644 --- a/app/policies/exercise_policy.rb +++ b/app/policies/exercise_policy.rb @@ -5,7 +5,7 @@ def batch_update? admin? end - %i[show? feedback? statistics? external_user_statistics? rfcs_for_exercise?].each do |action| + %i[show? feedback? statistics? external_user_statistics? rfcs_for_exercise? download_proforma?].each do |action| define_method(action) { admin? || teacher_in_study_group? || (teacher? && @record.public?) || author? } end diff --git a/app/views/exercises/index.html.slim b/app/views/exercises/index.html.slim index cd12890ba..8b78290a1 100644 --- a/app/views/exercises/index.html.slim +++ b/app/views/exercises/index.html.slim @@ -54,6 +54,7 @@ h1 = Exercise.model_name.human(count: :other) li = link_to(t('shared.destroy'), exercise, data: {confirm: t('shared.confirm_destroy')}, method: :delete, class: 'dropdown-item') if policy(exercise).destroy? li = link_to(t('.clone'), clone_exercise_path(exercise), data: {confirm: t('shared.confirm_destroy')}, method: :post, class: 'dropdown-item') if policy(exercise).clone? li = link_to(t('exercises.export_codeharbor.label'), '', class: 'dropdown-item export-start', data: {'exercise-id': exercise.id}) if policy(exercise).export_external_confirm? + li = link_to(t('exercises.download_proforma.label'), download_proforma_exercise_path(exercise), class: 'dropdown-item', target: '_blank', rel: 'noopener noreferrer') if policy(exercise).download_proforma? = render('shared/pagination', collection: @exercises) p = render('shared/new_button', model: Exercise) diff --git a/app/views/exercises/show.html.slim b/app/views/exercises/show.html.slim index 0d0a940d1..2eb8ca3b2 100644 --- a/app/views/exercises/show.html.slim +++ b/app/views/exercises/show.html.slim @@ -23,6 +23,7 @@ h1.d-inline-block li = link_to(t('shared.destroy'), @exercise, data: {confirm: t('shared.confirm_destroy')}, method: :delete, class: 'dropdown-item') if policy(@exercise).destroy? li = link_to(t('exercises.index.clone'), clone_exercise_path(@exercise), data: {confirm: t('shared.confirm_destroy')}, method: :post, class: 'dropdown-item') if policy(@exercise).clone? li = link_to(t('exercises.export_codeharbor.label'), '', class: 'dropdown-item export-start', data: {'exercise-id': @exercise.id}) if policy(@exercise).export_external_confirm? + li = link_to(t('exercises.download_proforma.label'), download_proforma_exercise_path(@exercise), class: 'dropdown-item', target: '_blank', rel: 'noopener noreferrer') if policy(@exercise).download_proforma? = row(label: 'exercise.title', value: @exercise.title) = row(label: 'exercise.internal_title', value: @exercise.internal_title) diff --git a/config/locales/de/exercise.yml b/config/locales/de/exercise.yml index 97b144bfb..97483a9ae 100644 --- a/config/locales/de/exercise.yml +++ b/config/locales/de/exercise.yml @@ -37,6 +37,8 @@ de: download_file_tree: file_root: Erstellte Dateien gone: Die angeforderte Datei konnte nicht abgerufen werden. Erstellte Dateien werden nur kurzzeitig vorgehalten und dann gelöscht. Bitte führen Sie den Code erneut aus und versuchen Sie dann wieder den Download der Datei. + download_proforma: + label: ProformaXML ZIP herunterladen editor: collapse_action_sidebar: Aktions-Leiste Einklappen collapse_output_sidebar: Ausgabe-Leiste Einklappen diff --git a/config/locales/en/exercise.yml b/config/locales/en/exercise.yml index 3f9e04b15..a10313f42 100644 --- a/config/locales/en/exercise.yml +++ b/config/locales/en/exercise.yml @@ -37,6 +37,8 @@ en: download_file_tree: file_root: Generated Files gone: The requested file could not be retrieved. Generated files are only held for a short time and are then deleted. Please run the code again and then try downloading the file a second time. + download_proforma: + label: Download ProformaXML ZIP editor: collapse_action_sidebar: Collapse Action Sidebar collapse_output_sidebar: Collapse Output Sidebar diff --git a/config/routes.rb b/config/routes.rb index ad064a050..e996728ec 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -91,6 +91,7 @@ get 'study_group_dashboard/:study_group_id', to: 'exercises#study_group_dashboard' post :export_external_check post :export_external_confirm + get :download_proforma end resources :programming_groups diff --git a/spec/controllers/exercises_controller_spec.rb b/spec/controllers/exercises_controller_spec.rb index af2eaa9c9..4b0b58898 100644 --- a/spec/controllers/exercises_controller_spec.rb +++ b/spec/controllers/exercises_controller_spec.rb @@ -518,4 +518,34 @@ end end end + + describe 'GET #download_proforma' do + subject(:get_request) { get :download_proforma, params: {id: exercise.id} } + + let(:zip) { instance_double(StringIO, string: 'dummy') } + + before do + allow(ProformaService::ExportTask).to receive(:call).with(exercise:).and_return(zip) + end + + it 'calls the ExportTask service' do + get_request + expect(ProformaService::ExportTask).to have_received(:call) + end + + it 'sends the correct data' do + get_request + expect(response.body).to eql 'dummy' + end + + it 'sets the correct Content-Type header' do + get_request + expect(response.header['Content-Type']).to eql 'application/zip' + end + + it 'sets the correct Content-Disposition header' do + get_request + expect(response.header['Content-Disposition']).to include "attachment; filename=\"exercise_#{exercise.id}.zip\"" + end + end end