wip multi-step export
This commit is contained in:
@ -208,6 +208,7 @@ $(document).on('turbolinks:load', function() {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var old_execution_environment = $('#exercise_execution_environment_id').val();
|
||||
var observeExecutionEnvironment = function() {
|
||||
$('#exercise_execution_environment_id').on('change', function(){
|
||||
@ -238,20 +239,25 @@ $(document).on('turbolinks:load', function() {
|
||||
|
||||
};
|
||||
|
||||
var observeExportStartButtons = function(){
|
||||
var observeExportButtons = function(){
|
||||
$('.export-start').on('click', function(e){
|
||||
e.preventDefault();
|
||||
$('#export-modal').modal({
|
||||
height: 250
|
||||
});
|
||||
$('#export-modal').modal('show');
|
||||
exportExerciseStart($(this).attr('data-exercise-id'));
|
||||
exportExerciseStart($(this).data().exerciseId);
|
||||
});
|
||||
$('body').on('click', '.export-retry-button', function(){
|
||||
exportExerciseStart($(this).data().exerciseId);
|
||||
});
|
||||
$('body').on('click', '.export-action', function(){
|
||||
exportExerciseConfirm($(this).data().exerciseId, $(this).data().exportType);
|
||||
});
|
||||
}
|
||||
|
||||
var exportExerciseStart = function(exerciseID) {
|
||||
var $exerciseDiv = $('#export-exercise');
|
||||
// var accountLinkID = $exerciseDiv.attr('data-account-link');
|
||||
var $messageDiv = $exerciseDiv.children('.export-message');
|
||||
var $actionsDiv = $exerciseDiv.children('.export-exercise-actions');
|
||||
|
||||
@ -261,9 +267,6 @@ $(document).on('turbolinks:load', function() {
|
||||
return $.ajax({
|
||||
type: 'POST',
|
||||
url: '/exercises/' + exerciseID + '/export_external_check',
|
||||
// data: {
|
||||
// account_link: accountLinkID
|
||||
// },
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
if (response.error) {
|
||||
@ -279,6 +282,37 @@ $(document).on('turbolinks:load', function() {
|
||||
});
|
||||
};
|
||||
|
||||
var exportExerciseConfirm = function(exerciseID, pushType) {
|
||||
var $exerciseDiv = $('#export-exercise');
|
||||
var $messageDiv = $exerciseDiv.children('.export-message');
|
||||
var $actionsDiv = $exerciseDiv.children('.export-exercise-actions');
|
||||
|
||||
return $.ajax({
|
||||
type: 'POST',
|
||||
url: '/exercises/' + exerciseID + '/export_external_confirm',
|
||||
data: {
|
||||
push_type: pushType
|
||||
},
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
$messageDiv.html(response.message)
|
||||
$actionsDiv.html(response.actions);
|
||||
|
||||
if(response.status == 'success') {
|
||||
$messageDiv.addClass('export-success');
|
||||
setTimeout((function() {
|
||||
$('#export-modal').modal('hide');
|
||||
$messageDiv.html('').removeClass('export-success');
|
||||
}), 3000);
|
||||
} else {
|
||||
$messageDiv.addClass('export-failure');
|
||||
}
|
||||
},
|
||||
error: function(a, b, c) {
|
||||
return alert('error:' + c);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var overrideTextareaTabBehavior = function() {
|
||||
$('.form-group textarea[name$="[content]"]').on('keydown', function(event) {
|
||||
@ -335,7 +369,7 @@ $(document).on('turbolinks:load', function() {
|
||||
// ignore tags table since it is in the dom before other tables
|
||||
if ($('table:not(#tags-table)').isPresent()) {
|
||||
enableBatchUpdate();
|
||||
observeExportStartButtons();
|
||||
observeExportButtons();
|
||||
} else if ($('.edit_exercise, .new_exercise').isPresent()) {
|
||||
execution_environments = $('form').data('execution-environments');
|
||||
file_types = $('form').data('file-types');
|
||||
|
@ -192,6 +192,14 @@ a.file-heading {
|
||||
flex-grow: 1;
|
||||
font-size: 12px;
|
||||
padding-right: 5px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
.export-message + :empty {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.export-exercise-actions:empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.export-exercise-actions {
|
||||
@ -203,3 +211,13 @@ a.file-heading {
|
||||
font-size: 12px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.export-success {
|
||||
color: darkgreen;
|
||||
font-size: 12pt;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.export-failure {
|
||||
color: darkred;
|
||||
}
|
||||
|
@ -7,14 +7,14 @@ 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 + [:push_proforma_xml, :clone, :implement, :working_times, :intervention, :search, :run, :statistics, :submit, :reload, :feedback, :study_group_dashboard, :export_external_check]
|
||||
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, :export_external_check, :export_external_confirm]
|
||||
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]
|
||||
|
||||
skip_before_action :verify_authenticity_token, only: [:import_proforma_xml, :import_uuid_check]
|
||||
skip_after_action :verify_authorized, only: [:import_proforma_xml, :import_uuid_check]
|
||||
skip_after_action :verify_policy_scoped, only: [:import_proforma_xml, :import_uuid_check], raise: false
|
||||
skip_before_action :verify_authenticity_token, only: [:import_proforma_xml, :import_uuid_check, :export_external_confirm]
|
||||
skip_after_action :verify_authorized, only: [:import_proforma_xml, :import_uuid_check, :export_external_confirm]
|
||||
skip_after_action :verify_policy_scoped, only: [:import_proforma_xml, :import_uuid_check, :export_external_confirm], raise: false
|
||||
|
||||
def authorize!
|
||||
authorize(@exercise || @exercises)
|
||||
@ -141,8 +141,8 @@ class ExercisesController < ApplicationController
|
||||
end
|
||||
response_hash = JSON.parse(response.body, symbolize_names: true)
|
||||
message = response_hash[:message]
|
||||
rescue Faraday::ClientError
|
||||
message = 'an error occured'
|
||||
rescue Faraday::Error => e
|
||||
message = t('exercises.export_codeharbor.error', message: e.message)
|
||||
error = true
|
||||
end
|
||||
|
||||
@ -154,15 +154,45 @@ class ExercisesController < ApplicationController
|
||||
exercise: @exercise,
|
||||
exercise_found: response_hash[:exercise_found],
|
||||
update_right: response_hash[:update_right],
|
||||
error: error
|
||||
error: error,
|
||||
exported: false
|
||||
}
|
||||
)
|
||||
|
||||
}, status: 200
|
||||
end
|
||||
|
||||
def export_external_confirm
|
||||
push_type = params[:push_type]
|
||||
|
||||
return render :fail unless %w[create_new export].include? push_type
|
||||
|
||||
@exercise.uuid = SecureRandom.uuid if push_type == 'create_new'
|
||||
|
||||
error = ExerciseService::PushExternal.call(
|
||||
zip: ProformaService::ExportTask.call(exercise: @exercise),
|
||||
codeharbor_link: current_user.codeharbor_link
|
||||
)
|
||||
if error.nil?
|
||||
render json: {
|
||||
status: 'success',
|
||||
message: t('exercises.export_codeharbor.successfully_exported', id: @exercise.id, title: @exercise.title),
|
||||
actions: render_to_string(partial: 'export_actions', locals: {exercise: @exercise, exported: true, error: error})
|
||||
}
|
||||
# @exercise, notice: t('controllers.exercise.push_external_notice', account_link: account_link.readable)
|
||||
else
|
||||
# logger.debug(error)
|
||||
render json: {
|
||||
status: 'fail',
|
||||
message: t('exercises.export_codeharbor.export_failed', id: @exercise.id, title: @exercise.title, error: error),
|
||||
actions: render_to_string(partial: 'export_actions', locals: {exercise: @exercise, exported: true, error: error})
|
||||
}
|
||||
# redirect_to @exercise, alert: t('controllers.account_links.not_working', account_link: account_link.readable)
|
||||
end
|
||||
end
|
||||
|
||||
def import_uuid_check
|
||||
user = user_for_oauth2_request
|
||||
user = user_from_api_key
|
||||
return render json: {}, status: 401 if user.nil?
|
||||
|
||||
uuid = params[:uuid]
|
||||
@ -179,7 +209,7 @@ class ExercisesController < ApplicationController
|
||||
tempfile.write request.body.read.force_encoding('UTF-8')
|
||||
tempfile.rewind
|
||||
|
||||
user = user_for_oauth2_request
|
||||
user = user_from_api_key
|
||||
return render json: {}, status: 401 if user.nil?
|
||||
|
||||
exercise = nil
|
||||
@ -192,15 +222,15 @@ class ExercisesController < ApplicationController
|
||||
render json: {}, status: 400
|
||||
end
|
||||
|
||||
def user_for_oauth2_request
|
||||
def user_from_api_key
|
||||
authorization_header = request.headers['Authorization']
|
||||
oauth2_token = authorization_header&.split(' ')&.second
|
||||
user_by_codeharbor_token(oauth2_token)
|
||||
api_key = authorization_header&.split(' ')&.second
|
||||
user_by_codeharbor_token(api_key)
|
||||
end
|
||||
private :user_for_oauth2_request
|
||||
private :user_from_api_key
|
||||
|
||||
def user_by_codeharbor_token(oauth2_token)
|
||||
link = CodeharborLink.where(oauth2token: oauth2_token)[0]
|
||||
def user_by_codeharbor_token(api_key)
|
||||
link = CodeharborLink.find_by_api_key(api_key)
|
||||
link&.user
|
||||
end
|
||||
private :user_by_codeharbor_token
|
||||
|
@ -7,7 +7,7 @@ class ExercisePolicy < AdminOrAuthorPolicy
|
||||
define_method(action) { admin? || teacher? }
|
||||
end
|
||||
|
||||
[:clone?, :destroy?, :edit?, :statistics?, :update?, :feedback?, :push_proforma_xml?, :export_external_check?].each do |action|
|
||||
[:clone?, :destroy?, :edit?, :statistics?, :update?, :feedback?, :push_proforma_xml?, :export_external_check?, :export_external_confirm?].each do |action|
|
||||
define_method(action) { admin? || author? }
|
||||
end
|
||||
|
||||
|
@ -9,19 +9,22 @@ module ExerciseService
|
||||
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
|
||||
conn = Faraday.new(url: CODEHARBOR_PUSH_LINK) do |faraday|
|
||||
faraday.adapter Faraday.default_adapter
|
||||
end
|
||||
|
||||
response = conn.post do |request|
|
||||
request.headers['Content-Type'] = 'application/zip'
|
||||
request.headers['Content-Length'] = body.length.to_s
|
||||
request.headers['Authorization'] = 'Bearer ' + @codeharbor_link.api_key
|
||||
request.body = body
|
||||
end
|
||||
|
||||
return response.success? ? nil : response.body
|
||||
rescue StandardError => e
|
||||
return e
|
||||
return e.message
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,20 +1,18 @@
|
||||
- if error
|
||||
= button_tag type: 'button', class:'btn btn-primary pull-right export-button', onclick: "exportExerciseStart(#{exercise.id})" do
|
||||
= button_tag type: 'button', class:'btn btn-primary pull-right export-button export-retry-button', data: {exercise_id: exercise.id} do
|
||||
i.fa.fa-refresh.confirm-icon
|
||||
= ' Retry'
|
||||
- else
|
||||
- if exercise_found
|
||||
- unless exported
|
||||
- if update_right
|
||||
= button_tag type: 'button', class:'btn btn-primary pull-right export-action export-button', data: {'export-type' => 'export'} do
|
||||
= button_tag type: 'button', class:'btn btn-primary pull-right export-action export-button', data: {exercise_id: exercise.id, export_type: 'export'} do
|
||||
i.fa.fa-check.confirm-icon
|
||||
= ' Overwrite'
|
||||
= button_tag type: 'button', class:'btn btn-primary pull-right export-action export-button', data: {'export-type' => 'create_new'} do
|
||||
i.fa.fa-check.confirm-icon-alt
|
||||
= ' Create new'
|
||||
- else
|
||||
= button_tag type: 'button', class:'btn btn-primary pull-right export-action export-button', data: {'export-type' => 'export'} do
|
||||
i.fa.fa-check.confirm-icon
|
||||
= ' Export'
|
||||
= ' Export'
|
||||
- else
|
||||
= button_tag type: 'button', class:'btn btn-primary pull-right export-action export-button', data: {exercise_id: exercise.id, export_type: 'create_new'} do
|
||||
i.fa.fa-check.confirm-icon
|
||||
= ' Create new'
|
||||
|
||||
= button_tag type: 'submit', class:'btn btn-secondary pull-right export-button', data: {dismiss: 'modal'} do
|
||||
i.fa.fa-remove.abort-icon
|
||||
= ' Abort'
|
||||
= exported ? ' Close' : ' Abort'
|
||||
|
@ -327,6 +327,9 @@ en:
|
||||
label: Export to Codeharbor
|
||||
success: Successfully pushed the exercise to CodeHarbor.
|
||||
dialogtitle: Export to Codeharbor
|
||||
successfully_exported: 'Exercise has successfully been exported.<br>ID: %{id}<br>Title: %{title}'
|
||||
export_failed: 'Export has failed.<br>ID: %{id}<br>Title: %{title}<br><br>Error: %{error}'
|
||||
error: 'An error occurred while contacting Codeharbor<br>Error: %{message}'
|
||||
file_form:
|
||||
hints:
|
||||
feedback_message: This message is used as a hint for failing tests.
|
||||
|
@ -87,6 +87,7 @@ Rails.application.routes.draw do
|
||||
get 'study_group_dashboard/:study_group_id', to: 'exercises#study_group_dashboard'
|
||||
post :push_proforma_xml
|
||||
post :export_external_check
|
||||
post :export_external_confirm
|
||||
end
|
||||
end
|
||||
|
||||
|
Reference in New Issue
Block a user