wip multi-step export

This commit is contained in:
Karol
2019-10-20 11:02:57 +02:00
parent 4ab78c170e
commit 7e7be4721a
8 changed files with 132 additions and 45 deletions

View File

@ -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');

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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.

View File

@ -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