diff --git a/Gemfile b/Gemfile index 47006ec7..c4d5897e 100644 --- a/Gemfile +++ b/Gemfile @@ -50,6 +50,7 @@ group :development, :staging do gem 'capistrano-rails' gem 'capistrano-rvm' gem 'capistrano-upload-config' + gem 'pry-rails' gem 'rack-mini-profiler' gem 'rubocop', require: false gem 'rubocop-rspec' diff --git a/Gemfile.lock b/Gemfile.lock index 7c80f248..2bfd7a2d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -226,6 +226,8 @@ GEM pry-byebug (3.7.0) byebug (~> 11.0) pry (~> 0.10) + pry-rails (0.3.9) + pry (>= 0.10.4) public_suffix (3.0.3) puma (3.12.1) pundit (2.0.1) @@ -441,6 +443,7 @@ DEPENDENCIES pg proforma! pry-byebug + pry-rails puma pundit rack-mini-profiler diff --git a/app/controllers/exercises_controller.rb b/app/controllers/exercises_controller.rb index b285454d..1421db5a 100644 --- a/app/controllers/exercises_controller.rb +++ b/app/controllers/exercises_controller.rb @@ -7,7 +7,7 @@ class ExercisesController < ApplicationController before_action :handle_file_uploads, only: [:create, :update] before_action :set_execution_environments, only: [:create, :edit, :new, :update] - before_action :set_exercise_and_authorize, only: MEMBER_ACTIONS + [:clone, :implement, :working_times, :intervention, :search, :run, :statistics, :submit, :reload, :feedback, :study_group_dashboard] + before_action :set_exercise_and_authorize, only: MEMBER_ACTIONS + [:push_proforma_xml, :clone, :implement, :working_times, :intervention, :search, :run, :statistics, :submit, :reload, :feedback, :study_group_dashboard] before_action :set_external_user_and_authorize, only: [:statistics] before_action :set_file_types, only: [:create, :edit, :new, :update] before_action :set_course_token, only: [:implement] @@ -108,16 +108,29 @@ class ExercisesController < ApplicationController end def push_proforma_xml - codeharbor_link = CodeharborLink.find(params[:account_link]) - oauth2_client = OAuth2::Client.new(codeharbor_link.client_id, codeharbor_link.client_secret, url: codeharbor_link.push_url, ssl: {verify: false}) - oauth2token = codeharbor_link[:oauth2token] - token = OAuth2::AccessToken.from_hash(oauth2_client, access_token: oauth2token) + # codeharbor_link = current_user.codeharbor_link # CodeharborLink.find(params[:account_link]) - # xml_generator = Proforma::XmlGenerator.new - xml_document = xml_generator.generate_xml(@exercise) - request = token.post(codeharbor_link.push_url, body: xml_document, headers: {'Content-Type' => 'text/xml'}) - puts request - redirect_to @exercise, notice: t('exercises.push_proforma_xml.notice', link: codeharbor_link.push_url) + error = ExerciseService::PushExternal.call( + zip: ProformaService::ExportTask.call(exercise: @exercise), + codeharbor_link: current_user.codeharbor_link + ) + if error.nil? + redirect_to exercises_path, notice: 'klappt' # t('controllers.exercise.push_external_notice', account_link: account_link.readable) + # redirect_to @exercise, notice: 'klappt' # t('controllers.exercise.push_external_notice', account_link: account_link.readable) + else + # logger.debug(error) + redirect_to exercises_path, alert: 'klappt nicht' # t('controllers.account_links.not_working', account_link: account_link.readable) + # redirect_to @exercise, alert: 'klappt nicht' # t('controllers.account_links.not_working', account_link: account_link.readable) + end + # oauth2_client = OAuth2::Client.new(codeharbor_link.client_id, codeharbor_link.client_secret, url: codeharbor_link.push_url, ssl: {verify: false}) + # oauth2token = codeharbor_link[:oauth2token] + # token = OAuth2::AccessToken.from_hash(oauth2_client, access_token: oauth2token) + + # # xml_generator = Proforma::XmlGenerator.new + # xml_document = xml_generator.generate_xml(@exercise) + # request = token.post(codeharbor_link.push_url, body: xml_document, headers: {'Content-Type' => 'text/xml'}) + # puts request + # redirect_to @exercise, notice: t('exercises.push_proforma_xml.notice', link: codeharbor_link.push_url) end def import_proforma_xml diff --git a/app/policies/exercise_policy.rb b/app/policies/exercise_policy.rb index 662349fe..8a5679ee 100644 --- a/app/policies/exercise_policy.rb +++ b/app/policies/exercise_policy.rb @@ -7,7 +7,7 @@ class ExercisePolicy < AdminOrAuthorPolicy define_method(action) { admin? || teacher? } end - [:clone?, :destroy?, :edit?, :statistics?, :update?, :feedback?].each do |action| + [:clone?, :destroy?, :edit?, :statistics?, :update?, :feedback?, :push_proforma_xml?].each do |action| define_method(action) { admin? || author? } end diff --git a/app/services/exercise_service/push_external.rb b/app/services/exercise_service/push_external.rb new file mode 100644 index 00000000..562ca57f --- /dev/null +++ b/app/services/exercise_service/push_external.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module ExerciseService + class PushExternal < ServiceBase + CODEHARBOR_PUSH_LINK = 'http://localhost:3001/import_exercise' + def initialize(zip:, codeharbor_link:) + @zip = zip + @codeharbor_link = codeharbor_link + end + + def execute + oauth2_client = OAuth2::Client.new(@codeharbor_link.client_id, @codeharbor_link.client_secret, site: CODEHARBOR_PUSH_LINK) + oauth2_token = @codeharbor_link[:oauth2token] + token = OAuth2::AccessToken.from_hash(oauth2_client, access_token: oauth2_token) + body = @zip.string + begin + token.post( + CODEHARBOR_PUSH_LINK, + body: body, + headers: {'Content-Type' => 'application/zip', 'Content-Length' => body.length.to_s} + ) + return nil + rescue StandardError => e + return e + end + end + end +end diff --git a/app/services/proforma_service/convert_exercise_to_task.rb b/app/services/proforma_service/convert_exercise_to_task.rb index 11c4b36c..1af8ac62 100644 --- a/app/services/proforma_service/convert_exercise_to_task.rb +++ b/app/services/proforma_service/convert_exercise_to_task.rb @@ -4,6 +4,8 @@ require 'mimemagic' module ProformaService class ConvertExerciseToTask < ServiceBase + DEFAULT_LANGUAGE = 'de' + def initialize(exercise: nil) @exercise = exercise end @@ -20,75 +22,63 @@ module ProformaService title: @exercise.title, description: @exercise.description, internal_description: @exercise.instructions, - # proglang: proglang, files: task_files, - # tests: tests, - # uuid: @exercise.uuid, + tests: tests, + uuid: uuid, # parent_uuid: parent_uuid, - # language: primary_description.language, - # model_solutions: model_solutions + language: DEFAULT_LANGUAGE, + model_solutions: model_solutions }.compact ) end - def parent_uuid - @exercise.clone_relations.first&.origin&.uuid - end - - def primary_description - @exercise.descriptions.select(&:primary?).first - end - - def proglang - {name: @exercise.execution_environment.language, version: @exercise.execution_environment.version} + def uuid + @exercise.update(uuid: SecureRandom.uuid) if @exercise.uuid.nil? + @exercise.uuid end def model_solutions - @exercise.exercise_files.filter { |file| file.role == 'Reference Implementation' }.map do |file| + @exercise.files.filter { |file| file.role == 'reference_implementation' }.map do |file| Proforma::ModelSolution.new( id: "ms-#{file.id}", - files: [ - Proforma::TaskFile.new( - id: file.id, - content: file.content, - filename: file.full_file_name, - used_by_grader: false, - usage_by_lms: 'display', - visible: 'delayed', - binary: false, - internal_description: file.role - ) - ] + files: model_solution_file(file) ) end end + def model_solution_file(file) + [ + task_file(file).tap do |ms_file| + ms_file.used_by_grader = false + ms_file.usage_by_lms = 'display' + ms_file.visible = 'delayed' + end + ] + end + def tests - @exercise.tests.map do |test| + @exercise.files.filter { |file| file.role == 'teacher_defined_test' }.map do |file| Proforma::Test.new( - id: test.id, - title: test.exercise_file.name, - files: test_file(test.exercise_file), + id: file.id, + title: file.name, + files: test_file(file), meta_data: { - 'feedback-message' => test.feedback_message, - 'testing-framework' => test.testing_framework&.name, - 'testing-framework-version' => test.testing_framework&.version + 'feedback-message' => file.feedback_message + # 'testing-framework' => test.testing_framework&.name, + # 'testing-framework-version' => test.testing_framework&.version }.compact ) end end def test_file(file) - [Proforma::TaskFile.new( - id: file.id, - content: file.content, - filename: file.full_file_name, - used_by_grader: true, - visible: file.hidden ? 'no' : 'yes', - binary: false, - internal_description: file.role || 'Teacher-defined Test' - )] + [ + 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 @@ -121,9 +111,5 @@ module ProformaService end ) end - - def attachment_content(file) - Paperclip.io_adapters.for(file.attachment).read - end end end diff --git a/app/services/proforma_service/convert_task_to_exercise.rb b/app/services/proforma_service/convert_task_to_exercise.rb index 75af90a8..f42c06da 100644 --- a/app/services/proforma_service/convert_task_to_exercise.rb +++ b/app/services/proforma_service/convert_task_to_exercise.rb @@ -21,7 +21,8 @@ module ProformaService title: @task.title, description: @task.description, instructions: @task.internal_description, - files: files + files: files, + uuid: @task.uuid ) end diff --git a/app/views/exercises/index.html.slim b/app/views/exercises/index.html.slim index 9c26ab6f..bdec864c 100644 --- a/app/views/exercises/index.html.slim +++ b/app/views/exercises/index.html.slim @@ -46,6 +46,7 @@ h1 = Exercise.model_name.human(count: 2) li = link_to(t('activerecord.models.user_exercise_feedback.other'), feedback_exercise_path(exercise), class: 'dropdown-item') if policy(exercise).feedback? 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'), push_proforma_xml_exercise_path(exercise), data: {confirm: 'PUSHPUSH?'}, method: :post, class: 'dropdown-item') if policy(exercise).push_proforma_xml? = 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 b9fd33bc..b75417c7 100644 --- a/app/views/exercises/show.html.slim +++ b/app/views/exercises/show.html.slim @@ -20,6 +20,7 @@ h1 = row(label: 'exercise.allow_file_creation', value: @exercise.allow_file_creation?) = row(label: 'exercise.allow_auto_completion', value: @exercise.allow_auto_completion?) = row(label: 'exercise.difficulty', value: @exercise.expected_difficulty) += row(label: 'exercise.uuid', value: @exercise.uuid) = row(label: 'exercise.tags', value: @exercise.exercise_tags.map{|et| "#{et.tag.name} (#{et.factor})"}.sort.join(", ")) = row(label: 'exercise.embedding_parameters', class: 'mb-4') do = content_tag(:input, nil, class: 'form-control mb-4', readonly: true, value: embedding_parameters(@exercise)) diff --git a/config/locales/en.yml b/config/locales/en.yml index ff8d8bb1..790e8393 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -44,6 +44,7 @@ en: allow_file_creation: "Allow file creation" difficulty: Difficulty token: "Exercise Token" + uuid: UUID proxy_exercise: title: Title files_count: Exercises Count @@ -315,6 +316,7 @@ en: request_for_comments_sent: "Request for comments sent." editor_file_tree: file_root: Files + export_codeharbor: Export to Codeharbor file_form: hints: feedback_message: This message is used as a hint for failing tests. diff --git a/config/routes.rb b/config/routes.rb index 9310e946..9277c4f0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -81,6 +81,7 @@ Rails.application.routes.draw do post :search get :statistics get :feedback + post :push_proforma_xml get :reload post :submit get 'study_group_dashboard/:study_group_id', to: 'exercises#study_group_dashboard' diff --git a/db/migrate/20190830142809_add_uuid_to_exercise.rb b/db/migrate/20190830142809_add_uuid_to_exercise.rb new file mode 100644 index 00000000..4dfa6e5c --- /dev/null +++ b/db/migrate/20190830142809_add_uuid_to_exercise.rb @@ -0,0 +1,5 @@ +class AddUuidToExercise < ActiveRecord::Migration[5.2] + def change + add_column :exercises, :uuid, :uuid + end +end diff --git a/db/schema.rb b/db/schema.rb index ec6a7366..d86763dd 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2019_08_18_104954) do +ActiveRecord::Schema.define(version: 2019_08_30_142809) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -153,6 +153,7 @@ ActiveRecord::Schema.define(version: 2019_08_18_104954) do t.boolean "allow_file_creation" t.boolean "allow_auto_completion", default: false t.integer "expected_difficulty", default: 1 + t.uuid "uuid" t.index ["id"], name: "index_exercises_on_id" end