create remote evaluation

This commit is contained in:
Niklas Kiefer
2017-02-02 18:14:33 +01:00
parent 252a6ba5d8
commit 3562aa9103
12 changed files with 327 additions and 6 deletions

View File

@ -443,6 +443,3 @@ DEPENDENCIES
uglifier (>= 1.3.0)
web-console (~> 2.0)
will_paginate (~> 3.0)
BUNDLED WITH
1.13.6

View File

@ -0,0 +1,88 @@
# run like this: powershell.exe -noprofile -executionpolicy bypass -file path\to\client_script.ps1 path\to\project_root
# CodeOcean Remote Client v0.4
#file_info format: <path/to/file/><file_name>=<id> (src/frog.java=34)
#file_path format: <path/to/file/><file_name>
param (
[string]$project_root
)
if ( !($project_root | select-string -pattern '[/\\]$') ){
$project_root += '/'
}
function post_web_request ($content_type, $data, $url){
$buffer = [System.Text.Encoding]::UTF8.GetBytes($data)
[System.Net.HttpWebRequest] $web_request = [System.Net.WebRequest]::Create($url)
$web_request.Method = 'POST'
$web_request.ContentType = $content_type
$web_request.ContentLength = $buffer.Length;
$request_stream = $web_request.GetRequestStream()
$request_stream.Write($buffer, 0, $buffer.Length)
$request_stream.Flush()
$request_stream.Close()
[System.Net.HttpWebResponse] $web_response = $web_request.GetResponse()
$stream_reader = new-object System.IO.StreamReader($web_response.GetResponseStream())
$result = $stream_reader.ReadToEnd()
return $result
}
function find_file ($file_name){
$search_result = get-childitem -recurse -path $project_root -filter $file_name
if( !$search_result.exists ){
write-host "Error: $file_name could not be found under $project_root."
exit 1
}elseif( $search_result.gettype().name -eq 'Object[]' ){
$search_result = $search_result[0]
}
return $search_result
}
function get_file ($file_path){
$path_to_file = $project_root
$path_to_file += ($file_path | select-string -pattern '^.+/').matches.value
$file_name = ($file_path | select-string -pattern '[^/]+$').matches.value
$file = get-childitem -path $path_to_file -filter $file_name
if( !$file.exists ){
write-host "Warning: $file_name should be in $path_to_file, but it is not. Searching whole project..."
$file = find_file $file_name
write-host 'Using '$file.fullname'.'
}
return $file
}
function get_escaped_file_content ($file){
$content = [IO.File]::ReadAllText($file.fullname)
$content = $content -replace "`r`n", '\n'
$content = $content -replace "`n", '\n'
$content = $content.replace('"', '\"')
return $content
}
function get_file_attributes ($file_info){
$file = get_file ($file_info | select-string -pattern '^.*(?==)').matches.value
$escaped_file_content = get_escaped_file_content $file
$file_id = ($file_info | select-string -pattern '[^=]+$').matches.value
return "{`"file_id`": $file_id,`"content`": `"$escaped_file_content`"}"
}
$co_file = get_file '.co'
$file_array = get-content $co_file.fullname
$validation_token = $file_array[0]
$files_attributes = get_file_attributes $file_array[1]
for ($i = 2; $i -lt $file_array.length; $i++){
$files_attributes += ', '
$files_attributes += get_file_attributes $file_array[$i]
}
$post_data = "{`"remote_evaluation`": {`"validation_token`": `"$validation_token`",`"files_attributes`": [$files_attributes]}}"
post_web_request 'application/json' $post_data 'http://codeocean.openhpi.de/evaluate'

View File

@ -0,0 +1,58 @@
#!/bin/bash
# CodeOcean Remote Client v0.4
#file_info format: <path/to/file/><file_name>=<id> (src/frog.java=34)
#file_path format: <path/to/file/><file_name>
project_root="${1%/}"
function get_valid_file_path {
file_path="$project_root/$1"
if [ -e "$file_path" ]; then
valid_file_path="$file_path"
else
file_name="${1##*/}"
valid_file_path="$(find "$project_root" -name "$file_name" | head -1)"
if ! [ "$valid_file_path" ]; then
path_to_file="$(echo "$1" | grep -oP '^.+/')"
echo "Error: $file_name is not in $project_root/$path_to_file and could not be found under $project_root."
exit 1
fi
fi
echo "$valid_file_path"
}
function get_escaped_file_content {
file_path="$1"
cat "$file_path" |
perl -p -e 's@\r\n@\\n@g' |
perl -p -e 's@\n@\\n@g' |
perl -p -e 's@"@\\"@g'
}
function get_file_attributes {
file_info="$1"
file_path="$(get_valid_file_path "${file_info%=*}")"
escaped_file_content="$(get_escaped_file_content "$file_path")"
file_id="${file_info##*=}"
echo "{\"file_id\": $file_id,\"content\": \"$escaped_file_content\"}"
}
co_file_path="$(get_valid_file_path '.co')"
mapfile -t file_array < "$co_file_path"
validation_token="${file_array[0]}"
files_attributes="$(get_file_attributes "${file_array[1]}")"
for ((i = 2; i < ${#file_array[@]}; i++)); do
files_attributes+=", $(get_file_attributes "${file_array[i]}")"
done
post_data="{\"remote_evaluation\": {\"validation_token\": \"$validation_token\",\"files_attributes\": [$files_attributes]}}"
curl -H 'Content-Type: application/json' --data "$post_data" http://codeocean.openhpi.de/evaluate
echo

View File

@ -0,0 +1,68 @@
#!/bin/bash
# CodeOcean Remote Client v0.4
#file_info format: <path/to/file/><file_name>=<id> (src/frog.java=34)
#file_path format: <path/to/file/><file_name>
project_root="${1%/}"
declare -a file_array
function get_valid_file_path {
file_path="$project_root/$1"
if [ -e "$file_path" ]; then
valid_file_path="$file_path"
else
file_name="${1##*/}"
valid_file_path="$(find "$project_root" -name "$file_name" | head -1)"
if ! [ "$valid_file_path" ]; then
path_to_file="$(echo "$1" | pcregrep -o '^.+/')"
echo "Error: $file_name is not in $project_root/$path_to_file and could not be found under $project_root."
exit 1
fi
fi
echo "$valid_file_path"
}
function get_escaped_file_content {
file_path="$1"
cat "$file_path" |
perl -p -e 's@\r\n@\\n@g' |
perl -p -e 's@\n@\\n@g' |
perl -p -e 's@"@\\"@g'
}
function get_file_attributes {
file_info="$1"
file_path="$(get_valid_file_path "${file_info%=*}")"
escaped_file_content="$(get_escaped_file_content "$file_path")"
file_id="${file_info##*=}"
echo "{\"file_id\": $file_id,\"content\": \"$escaped_file_content\"}"
}
function read_file_to_array {
let i=0
while IFS=$'\n' read -r line_data; do
file_array[i]="${line_data}"
((++i))
done < $1
}
co_file_path="$(get_valid_file_path '.co')"
read_file_to_array $co_file_path
validation_token="${file_array[0]}"
files_attributes="$(get_file_attributes "${file_array[1]}")"
for ((i = 2; i < ${#file_array[@]}; i++)); do
files_attributes+=", $(get_file_attributes "${file_array[i]}")"
done
post_data="{\"remote_evaluation\": {\"validation_token\": \"$validation_token\",\"files_attributes\": [$files_attributes]}}"
#echo ${post_data}
curl -H 'Content-Type: application/json' --data "$post_data" http://codeocean.openhpi.de/evaluate
echo

View File

@ -0,0 +1,8 @@
module RemoteEvaluationParameters
include FileParameters
def remote_evaluation_params
remote_evaluation_params = params[:remote_evaluation].permit(:validation_token, files_attributes: file_attributes)
end
private :remote_evaluation_params
end

View File

@ -0,0 +1,35 @@
class RemoteEvaluationController < ApplicationController
include RemoteEvaluationParameters
include SubmissionScoring
skip_after_action :verify_authorized
skip_before_action :verify_authenticity_token
# POST /evaluate
# @param validation_token
# @param files_attributes
def evaluate
validation_token = remote_evaluation_params[:validation_token]
files_attributes = remote_evaluation_params[:files_attributes] || []
# todo extra: validiere, ob files wirklich zur Übung gehören (wenn allowNewFiles-flag nicht gesetzt ist)
if (remote_evaluation_mapping = RemoteEvaluationMapping.find_by(:validation_token => validation_token))
puts remote_evaluation_mapping.exercise_id
puts remote_evaluation_mapping.user_id
_params = remote_evaluation_params.except(:validation_token)
_params[:exercise_id] = remote_evaluation_mapping.exercise_id
_params[:user_id] = remote_evaluation_mapping.user_id
_params[:cause] = "remoteAssess"
_params[:user_type] = "ExternalUser"
@submission = Submission.create(_params)
render json: score_submission(@submission)
else
# todo: better output
# todo: check token expired?
render json: "No exercise found for this validation_token! Please keep out!"
end
end
end

View File

@ -57,12 +57,29 @@ class SubmissionsController < ApplicationController
# files = @submission.files.map{ }
# zipline( files, 'submission.zip')
# send_data(@file.content, filename: @file.name_with_extension)
id_file = create_remote_evaluation_mapping
require 'zip'
stringio = Zip::OutputStream.write_buffer do |zio|
@files.each do |file|
zio.put_next_entry(file.name_with_extension)
zio.put_next_entry(file.path.to_s == '' ? file.name_with_extension : File.join(file.path, file.name_with_extension))
zio.write(file.content)
end
# zip .co file
zio.put_next_entry(File.basename id_file)
zio.write(File.read id_file)
File.delete(id_file) if File.exist?(id_file)
# zip client scripts
scripts_path = 'app/assets/remote_scripts'
Dir.foreach(scripts_path) do |file|
next if file == '.' or file == '..'
zio.put_next_entry(File.join('.scripts', File.basename(file)))
zio.write(File.read File.join(scripts_path, file))
end
end
send_data(stringio.string, filename: @submission.exercise.title.tr(" ", "_") + ".zip")
end
@ -316,4 +333,23 @@ class SubmissionsController < ApplicationController
server_sent_event.close
end
private :with_server_sent_events
end
def create_remote_evaluation_mapping
user_id = @submission.user_id
exercise_id = @submission.exercise_id
remote_evaluation_mapping = RemoteEvaluationMapping.create(:user_id => user_id, :exercise_id => exercise_id)
# create id.co file
path = "tmp/.co"
content = "#{remote_evaluation_mapping.validation_token}\n"
@submission.files.each do |file|
file_path = file.path.to_s == '' ? file.name_with_extension : File.join(file.path, file.name_with_extension)
content += "#{file_path}=#{file.id.to_s}\n"
end
File.open(path, "w+") do |f|
f.write(content)
end
path
end
end

View File

@ -0,0 +1,10 @@
# todo: reference to lti_param_model
class RemoteEvaluationMapping < ActiveRecord::Base
before_create :generate_token, unless: :validation_token?
belongs_to :exercise
belongs_to :user
def generate_token
self.validation_token = SecureRandom.urlsafe_base64
end
end

View File

@ -2,7 +2,7 @@ class Submission < ActiveRecord::Base
include Context
include Creation
CAUSES = %w(assess download file render run save submit test autosave requestComments)
CAUSES = %w(assess download file render run save submit test autosave requestComments remoteAssess)
FILENAME_URL_PLACEHOLDER = '{filename}'
belongs_to :exercise

View File

@ -105,4 +105,6 @@ Rails.application.routes.draw do
end
end
post "/evaluate", to: 'remote_evaluation#evaluate', via: [:post]
end

View File

@ -0,0 +1,11 @@
class CreateRemoteEvaluationMappings < ActiveRecord::Migration
def change
create_table :remote_evaluation_mappings do |t|
t.integer "user_id", null: false
t.integer "exercise_id", null: false
t.string "validation_token", null: false
t.datetime "created_at"
t.datetime "updated_at"
end
end
end

View File

@ -191,6 +191,14 @@ ActiveRecord::Schema.define(version: 20161214144837) do
t.datetime "updated_at"
end
create_table "remote_evaluation_mappings", force: :cascade do |t|
t.integer "user_id", null: false
t.integer "exercise_id", null: false
t.string "validation_token", null: false
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "request_for_comments", force: :cascade do |t|
t.integer "user_id", null: false
t.integer "exercise_id", null: false