enable export to codeharbor
This commit is contained in:
1
Gemfile
1
Gemfile
@ -50,6 +50,7 @@ group :development, :staging do
|
|||||||
gem 'capistrano-rails'
|
gem 'capistrano-rails'
|
||||||
gem 'capistrano-rvm'
|
gem 'capistrano-rvm'
|
||||||
gem 'capistrano-upload-config'
|
gem 'capistrano-upload-config'
|
||||||
|
gem 'pry-rails'
|
||||||
gem 'rack-mini-profiler'
|
gem 'rack-mini-profiler'
|
||||||
gem 'rubocop', require: false
|
gem 'rubocop', require: false
|
||||||
gem 'rubocop-rspec'
|
gem 'rubocop-rspec'
|
||||||
|
@ -226,6 +226,8 @@ GEM
|
|||||||
pry-byebug (3.7.0)
|
pry-byebug (3.7.0)
|
||||||
byebug (~> 11.0)
|
byebug (~> 11.0)
|
||||||
pry (~> 0.10)
|
pry (~> 0.10)
|
||||||
|
pry-rails (0.3.9)
|
||||||
|
pry (>= 0.10.4)
|
||||||
public_suffix (3.0.3)
|
public_suffix (3.0.3)
|
||||||
puma (3.12.1)
|
puma (3.12.1)
|
||||||
pundit (2.0.1)
|
pundit (2.0.1)
|
||||||
@ -441,6 +443,7 @@ DEPENDENCIES
|
|||||||
pg
|
pg
|
||||||
proforma!
|
proforma!
|
||||||
pry-byebug
|
pry-byebug
|
||||||
|
pry-rails
|
||||||
puma
|
puma
|
||||||
pundit
|
pundit
|
||||||
rack-mini-profiler
|
rack-mini-profiler
|
||||||
|
@ -7,7 +7,7 @@ class ExercisesController < ApplicationController
|
|||||||
|
|
||||||
before_action :handle_file_uploads, only: [:create, :update]
|
before_action :handle_file_uploads, only: [:create, :update]
|
||||||
before_action :set_execution_environments, only: [:create, :edit, :new, :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_external_user_and_authorize, only: [:statistics]
|
||||||
before_action :set_file_types, only: [:create, :edit, :new, :update]
|
before_action :set_file_types, only: [:create, :edit, :new, :update]
|
||||||
before_action :set_course_token, only: [:implement]
|
before_action :set_course_token, only: [:implement]
|
||||||
@ -108,16 +108,29 @@ class ExercisesController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def push_proforma_xml
|
def push_proforma_xml
|
||||||
codeharbor_link = CodeharborLink.find(params[:account_link])
|
# codeharbor_link = current_user.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)
|
|
||||||
|
|
||||||
# xml_generator = Proforma::XmlGenerator.new
|
error = ExerciseService::PushExternal.call(
|
||||||
xml_document = xml_generator.generate_xml(@exercise)
|
zip: ProformaService::ExportTask.call(exercise: @exercise),
|
||||||
request = token.post(codeharbor_link.push_url, body: xml_document, headers: {'Content-Type' => 'text/xml'})
|
codeharbor_link: current_user.codeharbor_link
|
||||||
puts request
|
)
|
||||||
redirect_to @exercise, notice: t('exercises.push_proforma_xml.notice', link: codeharbor_link.push_url)
|
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
|
end
|
||||||
|
|
||||||
def import_proforma_xml
|
def import_proforma_xml
|
||||||
|
@ -7,7 +7,7 @@ class ExercisePolicy < AdminOrAuthorPolicy
|
|||||||
define_method(action) { admin? || teacher? }
|
define_method(action) { admin? || teacher? }
|
||||||
end
|
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? }
|
define_method(action) { admin? || author? }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
28
app/services/exercise_service/push_external.rb
Normal file
28
app/services/exercise_service/push_external.rb
Normal file
@ -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
|
@ -4,6 +4,8 @@ require 'mimemagic'
|
|||||||
|
|
||||||
module ProformaService
|
module ProformaService
|
||||||
class ConvertExerciseToTask < ServiceBase
|
class ConvertExerciseToTask < ServiceBase
|
||||||
|
DEFAULT_LANGUAGE = 'de'
|
||||||
|
|
||||||
def initialize(exercise: nil)
|
def initialize(exercise: nil)
|
||||||
@exercise = exercise
|
@exercise = exercise
|
||||||
end
|
end
|
||||||
@ -20,75 +22,63 @@ module ProformaService
|
|||||||
title: @exercise.title,
|
title: @exercise.title,
|
||||||
description: @exercise.description,
|
description: @exercise.description,
|
||||||
internal_description: @exercise.instructions,
|
internal_description: @exercise.instructions,
|
||||||
|
|
||||||
# proglang: proglang,
|
# proglang: proglang,
|
||||||
files: task_files,
|
files: task_files,
|
||||||
# tests: tests,
|
tests: tests,
|
||||||
# uuid: @exercise.uuid,
|
uuid: uuid,
|
||||||
# parent_uuid: parent_uuid,
|
# parent_uuid: parent_uuid,
|
||||||
# language: primary_description.language,
|
language: DEFAULT_LANGUAGE,
|
||||||
# model_solutions: model_solutions
|
model_solutions: model_solutions
|
||||||
}.compact
|
}.compact
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def parent_uuid
|
def uuid
|
||||||
@exercise.clone_relations.first&.origin&.uuid
|
@exercise.update(uuid: SecureRandom.uuid) if @exercise.uuid.nil?
|
||||||
end
|
@exercise.uuid
|
||||||
|
|
||||||
def primary_description
|
|
||||||
@exercise.descriptions.select(&:primary?).first
|
|
||||||
end
|
|
||||||
|
|
||||||
def proglang
|
|
||||||
{name: @exercise.execution_environment.language, version: @exercise.execution_environment.version}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def model_solutions
|
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(
|
Proforma::ModelSolution.new(
|
||||||
id: "ms-#{file.id}",
|
id: "ms-#{file.id}",
|
||||||
files: [
|
files: model_solution_file(file)
|
||||||
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
|
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
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
|
def tests
|
||||||
@exercise.tests.map do |test|
|
@exercise.files.filter { |file| file.role == 'teacher_defined_test' }.map do |file|
|
||||||
Proforma::Test.new(
|
Proforma::Test.new(
|
||||||
id: test.id,
|
id: file.id,
|
||||||
title: test.exercise_file.name,
|
title: file.name,
|
||||||
files: test_file(test.exercise_file),
|
files: test_file(file),
|
||||||
meta_data: {
|
meta_data: {
|
||||||
'feedback-message' => test.feedback_message,
|
'feedback-message' => file.feedback_message
|
||||||
'testing-framework' => test.testing_framework&.name,
|
# 'testing-framework' => test.testing_framework&.name,
|
||||||
'testing-framework-version' => test.testing_framework&.version
|
# 'testing-framework-version' => test.testing_framework&.version
|
||||||
}.compact
|
}.compact
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_file(file)
|
def test_file(file)
|
||||||
[Proforma::TaskFile.new(
|
[
|
||||||
id: file.id,
|
task_file(file).tap do |t_file|
|
||||||
content: file.content,
|
t_file.used_by_grader = true
|
||||||
filename: file.full_file_name,
|
t_file.internal_description = 'teacher_defined_test'
|
||||||
used_by_grader: true,
|
end
|
||||||
visible: file.hidden ? 'no' : 'yes',
|
]
|
||||||
binary: false,
|
|
||||||
internal_description: file.role || 'Teacher-defined Test'
|
|
||||||
)]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def task_files
|
def task_files
|
||||||
@ -121,9 +111,5 @@ module ProformaService
|
|||||||
end
|
end
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def attachment_content(file)
|
|
||||||
Paperclip.io_adapters.for(file.attachment).read
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -21,7 +21,8 @@ module ProformaService
|
|||||||
title: @task.title,
|
title: @task.title,
|
||||||
description: @task.description,
|
description: @task.description,
|
||||||
instructions: @task.internal_description,
|
instructions: @task.internal_description,
|
||||||
files: files
|
files: files,
|
||||||
|
uuid: @task.uuid
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -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('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('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('.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)
|
= render('shared/pagination', collection: @exercises)
|
||||||
p = render('shared/new_button', model: Exercise)
|
p = render('shared/new_button', model: Exercise)
|
||||||
|
@ -20,6 +20,7 @@ h1
|
|||||||
= row(label: 'exercise.allow_file_creation', value: @exercise.allow_file_creation?)
|
= 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.allow_auto_completion', value: @exercise.allow_auto_completion?)
|
||||||
= row(label: 'exercise.difficulty', value: @exercise.expected_difficulty)
|
= 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.tags', value: @exercise.exercise_tags.map{|et| "#{et.tag.name} (#{et.factor})"}.sort.join(", "))
|
||||||
= row(label: 'exercise.embedding_parameters', class: 'mb-4') do
|
= row(label: 'exercise.embedding_parameters', class: 'mb-4') do
|
||||||
= content_tag(:input, nil, class: 'form-control mb-4', readonly: true, value: embedding_parameters(@exercise))
|
= content_tag(:input, nil, class: 'form-control mb-4', readonly: true, value: embedding_parameters(@exercise))
|
||||||
|
@ -44,6 +44,7 @@ en:
|
|||||||
allow_file_creation: "Allow file creation"
|
allow_file_creation: "Allow file creation"
|
||||||
difficulty: Difficulty
|
difficulty: Difficulty
|
||||||
token: "Exercise Token"
|
token: "Exercise Token"
|
||||||
|
uuid: UUID
|
||||||
proxy_exercise:
|
proxy_exercise:
|
||||||
title: Title
|
title: Title
|
||||||
files_count: Exercises Count
|
files_count: Exercises Count
|
||||||
@ -315,6 +316,7 @@ en:
|
|||||||
request_for_comments_sent: "Request for comments sent."
|
request_for_comments_sent: "Request for comments sent."
|
||||||
editor_file_tree:
|
editor_file_tree:
|
||||||
file_root: Files
|
file_root: Files
|
||||||
|
export_codeharbor: Export to Codeharbor
|
||||||
file_form:
|
file_form:
|
||||||
hints:
|
hints:
|
||||||
feedback_message: This message is used as a hint for failing tests.
|
feedback_message: This message is used as a hint for failing tests.
|
||||||
|
@ -81,6 +81,7 @@ Rails.application.routes.draw do
|
|||||||
post :search
|
post :search
|
||||||
get :statistics
|
get :statistics
|
||||||
get :feedback
|
get :feedback
|
||||||
|
post :push_proforma_xml
|
||||||
get :reload
|
get :reload
|
||||||
post :submit
|
post :submit
|
||||||
get 'study_group_dashboard/:study_group_id', to: 'exercises#study_group_dashboard'
|
get 'study_group_dashboard/:study_group_id', to: 'exercises#study_group_dashboard'
|
||||||
|
5
db/migrate/20190830142809_add_uuid_to_exercise.rb
Normal file
5
db/migrate/20190830142809_add_uuid_to_exercise.rb
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
class AddUuidToExercise < ActiveRecord::Migration[5.2]
|
||||||
|
def change
|
||||||
|
add_column :exercises, :uuid, :uuid
|
||||||
|
end
|
||||||
|
end
|
@ -10,7 +10,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# 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
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "plpgsql"
|
enable_extension "plpgsql"
|
||||||
@ -153,6 +153,7 @@ ActiveRecord::Schema.define(version: 2019_08_18_104954) do
|
|||||||
t.boolean "allow_file_creation"
|
t.boolean "allow_file_creation"
|
||||||
t.boolean "allow_auto_completion", default: false
|
t.boolean "allow_auto_completion", default: false
|
||||||
t.integer "expected_difficulty", default: 1
|
t.integer "expected_difficulty", default: 1
|
||||||
|
t.uuid "uuid"
|
||||||
t.index ["id"], name: "index_exercises_on_id"
|
t.index ["id"], name: "index_exercises_on_id"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user