From e81b04da62de917385e4a07476639a260c90ea7a Mon Sep 17 00:00:00 2001 From: karol Date: Thu, 26 Jun 2025 00:10:16 +0200 Subject: [PATCH] Add ProFormA validity checks and disable exporting not invalid --- app/assets/stylesheets/tasks.css.scss | 10 +++ .../task_contributions_controller.rb | 1 + app/controllers/tasks_controller.rb | 1 + app/services/proforma_service/validation.rb | 26 +++++++ app/views/tasks/show.html.slim | 23 +++++-- config/locales/de/views/tasks.yml | 1 + config/locales/en/views/tasks.yml | 1 + spec/controllers/tasks_controller_spec.rb | 19 +++++ .../proforma_service/validation_spec.rb | 69 +++++++++++++++++++ 9 files changed, 147 insertions(+), 4 deletions(-) create mode 100644 app/services/proforma_service/validation.rb create mode 100644 spec/services/proforma_service/validation_spec.rb diff --git a/app/assets/stylesheets/tasks.css.scss b/app/assets/stylesheets/tasks.css.scss index 08ee4c966..1da117d2e 100644 --- a/app/assets/stylesheets/tasks.css.scss +++ b/app/assets/stylesheets/tasks.css.scss @@ -110,6 +110,16 @@ margin-bottom: 0 !important; } } +.button-box{ + + .btn.disabled{ + border: 1px solid lightgray; + } +} + +.disabled-btn-wrapper { + width: 100%; +} .completeness-checklist-container { background: white; diff --git a/app/controllers/task_contributions_controller.rb b/app/controllers/task_contributions_controller.rb index 1a305091f..ca43165aa 100644 --- a/app/controllers/task_contributions_controller.rb +++ b/app/controllers/task_contributions_controller.rb @@ -45,6 +45,7 @@ def show @files = @task.files @tests = @task.tests @model_solutions = @task.model_solutions + @proforma_valid = ProformaService::Validation.call(task: @task) @user_rating = @task.ratings&.find_by(user: current_user)&.rating render 'tasks/show' diff --git a/app/controllers/tasks_controller.rb b/app/controllers/tasks_controller.rb index 81105a8a0..48c38a46e 100644 --- a/app/controllers/tasks_controller.rb +++ b/app/controllers/tasks_controller.rb @@ -38,6 +38,7 @@ def show @tests = @task.tests @model_solutions = @task.model_solutions + @proforma_valid = ProformaService::Validation.call(task: @task) @user_rating = @task.ratings&.find_by(user: current_user) || Rating.new(Rating::CATEGORIES.index_with {|_category| 0 }) end diff --git a/app/services/proforma_service/validation.rb b/app/services/proforma_service/validation.rb new file mode 100644 index 000000000..47dd3ac31 --- /dev/null +++ b/app/services/proforma_service/validation.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module ProformaService + class Validation < ServiceBase + def initialize(task:) + super() + @task = task + end + + # Returns a hash: { => valid?, nil => all_versions_valid? } + def execute + result = ProformaXML::SCHEMA_VERSIONS.index_with {|version| version_valid?(version:) } + result[nil] = result.values.all? + result + end + + private + + def version_valid?(version:) + ProformaService::ExportTask.call(task: @task, options: {version:}) + true + rescue ProformaXML::PostGenerateValidationError + false + end + end +end diff --git a/app/views/tasks/show.html.slim b/app/views/tasks/show.html.slim index bbcd5be97..7a170901e 100644 --- a/app/views/tasks/show.html.slim +++ b/app/views/tasks/show.html.slim @@ -519,13 +519,22 @@ .wrapper - if policy(@task).download? .dropdown.btn-group - = button_tag class: 'btn btn-light dropdown-toggle', data: {bs_toggle: 'dropdown'} do - = t('common.button.download_zip') + - if @proforma_valid[nil] + = button_tag class: 'btn btn-light dropdown-toggle', data: {bs_toggle: 'dropdown'} do + = t('common.button.download_zip') + - else + .disabled-btn-wrapper data-bs-toggle='tooltip' title=t('.not_proforma_valid', version: '') + = button_tag class: 'btn btn-light dropdown-toggle disabled', 'data-bs-toggle': 'dropdown' do + = t('common.button.download_zip') ul.scrollable.dropdown-menu role='menu' li.dropdown-header = "#{t('common.button.available_versions')}: " - ProformaXML::SCHEMA_VERSIONS.each do |proforma_version| li - = link_to(proforma_version, download_task_path(@task, version: proforma_version), class: 'btn btn-light dropdown-item', target: '_blank', rel: 'noopener noreferrer') + - if @proforma_valid[proforma_version] + = link_to proforma_version, download_task_path(@task, version: proforma_version), class: 'btn btn-light dropdown-item', target: '_blank', rel: 'noopener noreferrer' + - else + .disabled-btn-wrapper data-bs-toggle='tooltip' title=t('.not_proforma_valid', version: " (#{proforma_version})") + = link_to(proforma_version, download_task_path(@task, version: proforma_version), class: 'btn btn-light dropdown-item disabled', target: '_blank', rel: 'noopener noreferrer') - else div data-bs-toggle='tooltip' title=unavailable_tooltip data-bs-delay=150 @@ -535,6 +544,7 @@ .dropdown.btn-group = button_tag class: 'btn btn-light dropdown-toggle', data: {bs_toggle: 'dropdown'} do = t('.button.export') + ul.scrollable.dropdown-menu role='menu' li.dropdown-header = "#{t('tasks.show.export_to')}: " - if current_user.available_account_links.empty? @@ -543,7 +553,12 @@ - else - current_user.available_account_links.each do |acc_link| li - = button_to(acc_link.name, export_external_start_task_path(account_link: acc_link), method: :post, remote: true, class: 'dropdown-item export-test') + - if @proforma_valid[acc_link.proforma_version || ProformaXML::SCHEMA_VERSION_LATEST] + = link_to(acc_link.name, export_external_start_task_path(account_link: acc_link), method: :post, remote: true, class: 'dropdown-item export-test') + - else + .disabled-btn-wrapper data-bs-toggle='tooltip' title=t('.not_proforma_valid', version: " (#{acc_link.proforma_version || ProformaXML::SCHEMA_VERSION_LATEST})") + = link_to(acc_link.name, export_external_start_task_path(account_link: acc_link), method: :post, remote: true, class: 'dropdown-item export-test disabled') + - else div data-bs-toggle='tooltip' title=unavailable_tooltip data-bs-delay=150 = button_tag class: 'btn btn-outline-dark dropdown-toggle disabled', data: {bs_toggle: 'dropdown'} do diff --git a/config/locales/de/views/tasks.yml b/config/locales/de/views/tasks.yml index cb886c0e3..94febb471 100644 --- a/config/locales/de/views/tasks.yml +++ b/config/locales/de/views/tasks.yml @@ -111,6 +111,7 @@ de: no_license: Keine no_model_solution_present: Keine Musterlösungen vorhanden no_tests: Keine Tests enthalten + not_proforma_valid: Die Aufgabe ist nicht in ProFormA%{version} exportierbar owner_required_tooltip: Sie müssen der Autor der Aufgabe sein, um die Funktion zu verwenden. remove_task_from_collection_warning: Sind Sie sicher, dass Sie diese Aufgabe aus der Sammlung entfernen wollen? task_contribution: diff --git a/config/locales/en/views/tasks.yml b/config/locales/en/views/tasks.yml index db6e57367..7cc7e5e56 100644 --- a/config/locales/en/views/tasks.yml +++ b/config/locales/en/views/tasks.yml @@ -111,6 +111,7 @@ en: no_license: None no_model_solution_present: No Model Solutions present no_tests: No Tests included + not_proforma_valid: The task is not exportable in ProFormA%{version}. owner_required_tooltip: This feature can only be used by the owner of the task. remove_task_from_collection_warning: Are you sure you want to remove this Task from the Collection? task_contribution: diff --git a/spec/controllers/tasks_controller_spec.rb b/spec/controllers/tasks_controller_spec.rb index dda4127c2..bfab5d7ac 100644 --- a/spec/controllers/tasks_controller_spec.rb +++ b/spec/controllers/tasks_controller_spec.rb @@ -219,6 +219,25 @@ expect(response).to redirect_to([contribution.base, contribution]) end end + + context 'when checking proforma validity' do + before do + stub_const('ProformaXML::SCHEMA_VERSIONS', ['2.0']) + + validation_result = {nil => true, '2.0' => false} + allow(ProformaService::Validation).to receive(:call).with(task: task).and_return(validation_result) + end + + it 'assigns proforma_valid to instance variable' do + get_request + expect(assigns(:proforma_valid)).to be_a(Hash) + end + + it 'includes all values in proforma_valid hash' do + get_request + expect(assigns(:proforma_valid)).to eql({nil => true, '2.0' => false}) + end + end end end diff --git a/spec/services/proforma_service/validation_spec.rb b/spec/services/proforma_service/validation_spec.rb new file mode 100644 index 000000000..fc352766b --- /dev/null +++ b/spec/services/proforma_service/validation_spec.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe ProformaService::Validation do + describe '.new' do + subject(:validation) do + described_class.new(task:) + end + + let(:task) { build(:task) } + + it 'assigns task' do + expect(validation.instance_variable_get(:@task)).to be task + end + end + + describe '.call' do + subject(:validation) { described_class.call(task:) } + + let(:task) { create(:task) } + + before do + stub_const('ProformaXML::SCHEMA_VERSIONS', ['2.0', '2.1']) + end + + context 'when the task is valid for all schema versions' do + before do + allow(ProformaService::ExportTask).to receive(:call).and_return(true) + end + + it 'returns a hash with all versions as valid' do + expect(validation[nil]).to be true + expect(validation['2.0']).to be true + expect(validation['2.1']).to be true + end + end + + context 'when the task is invalid for a schema version' do + before do + allow(ProformaService::ExportTask).to receive(:call).and_raise(ProformaXML::PostGenerateValidationError, '["not valid"]') + end + + it 'returns a hash with all versions as invalid' do + expect(validation[nil]).to be false + expect(validation['2.0']).to be false + expect(validation['2.1']).to be false + end + end + + context 'when some versions are valid and others are invalid' do + before do + allow(ProformaService::ExportTask).to receive(:call) + .with(task: task, options: {version: '2.0'}) + .and_return(true) + + allow(ProformaService::ExportTask).to receive(:call) + .with(task: task, options: {version: '2.1'}) + .and_raise(ProformaXML::PostGenerateValidationError, '["not valid"]') + end + + it 'returns a hash with correct validity for each version' do + expect(validation['2.0']).to be true + expect(validation['2.1']).to be false + expect(validation[nil]).to be false + end + end + end +end