diff --git a/Gemfile b/Gemfile
index e77ae876..5c6bbd31 100644
--- a/Gemfile
+++ b/Gemfile
@@ -35,6 +35,7 @@ gem 'uglifier', '>= 1.3.0'
gem 'will_paginate', '~> 3.0'
gem 'tubesock'
gem 'faye-websocket'
+gem 'nokogiri'
group :development do
gem 'better_errors', platform: :ruby
diff --git a/Gemfile.lock b/Gemfile.lock
index 268f8596..5feafdc7 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -2,41 +2,40 @@ GEM
remote: https://rubygems.org/
specs:
ZenTest (4.11.0)
- actionmailer (4.1.14)
- actionpack (= 4.1.14)
- actionview (= 4.1.14)
+ actionmailer (4.1.14.1)
+ actionpack (= 4.1.14.1)
+ actionview (= 4.1.14.1)
mail (~> 2.5, >= 2.5.4)
- actionpack (4.1.14)
- actionview (= 4.1.14)
- activesupport (= 4.1.14)
+ actionpack (4.1.14.1)
+ actionview (= 4.1.14.1)
+ activesupport (= 4.1.14.1)
rack (~> 1.5.2)
rack-test (~> 0.6.2)
- actionview (4.1.14)
- activesupport (= 4.1.14)
+ actionview (4.1.14.1)
+ activesupport (= 4.1.14.1)
builder (~> 3.1)
erubis (~> 2.7.0)
- activemodel (4.1.14)
- activesupport (= 4.1.14)
+ activemodel (4.1.14.1)
+ activesupport (= 4.1.14.1)
builder (~> 3.1)
- activerecord (4.1.14)
- activemodel (= 4.1.14)
- activesupport (= 4.1.14)
+ activerecord (4.1.14.1)
+ activemodel (= 4.1.14.1)
+ activesupport (= 4.1.14.1)
arel (~> 5.0.0)
- activerecord-jdbc-adapter (1.3.15)
+ activerecord-jdbc-adapter (1.3.19)
activerecord (>= 2.2)
- activerecord-jdbcpostgresql-adapter (1.3.15)
- activerecord-jdbc-adapter (~> 1.3.15)
+ activerecord-jdbcpostgresql-adapter (1.3.19)
+ activerecord-jdbc-adapter (~> 1.3.19)
jdbc-postgres (>= 9.1)
- activesupport (4.1.14)
+ activesupport (4.1.14.1)
i18n (~> 0.6, >= 0.6.9)
json (~> 1.7, >= 1.7.7)
minitest (~> 5.1)
thread_safe (~> 0.1)
tzinfo (~> 1.1)
+ addressable (2.4.0)
arel (5.0.1.20140414130214)
- ast (2.0.0)
- astrolabe (1.3.0)
- parser (>= 2.2.0.pre.3, < 3.0)
+ ast (2.2.0)
autotest-rails (4.2.1)
ZenTest (~> 4.5)
bcrypt (3.1.10)
@@ -50,7 +49,7 @@ GEM
bootstrap-will_paginate (0.0.10)
will_paginate
builder (3.2.2)
- byebug (6.0.2)
+ byebug (8.2.2)
capistrano (3.3.5)
capistrano-stats (~> 1.1.0)
i18n
@@ -59,19 +58,20 @@ GEM
capistrano-bundler (1.1.4)
capistrano (~> 3.1)
sshkit (~> 1.2)
- capistrano-rails (1.1.2)
+ capistrano-rails (1.1.6)
capistrano (~> 3.1)
capistrano-bundler (~> 1.1)
capistrano-rvm (0.1.2)
capistrano (~> 3.0)
sshkit (~> 1.2)
capistrano-stats (1.1.1)
- capistrano-upload-config (0.6.0)
+ capistrano-upload-config (0.7.0)
capistrano (>= 3.0)
- capistrano3-puma (0.9.0)
+ capistrano3-puma (1.2.1)
capistrano (~> 3.0)
puma (>= 2.6)
- capybara (2.4.4)
+ capybara (2.6.2)
+ addressable
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
rack (>= 1.0.0)
@@ -82,9 +82,9 @@ GEM
activesupport (>= 3.2.0)
json (>= 1.7)
mime-types (>= 1.16)
- childprocess (0.5.6)
+ childprocess (0.5.9)
ffi (~> 1.0, >= 1.0.11)
- codeclimate-test-reporter (0.4.7)
+ codeclimate-test-reporter (0.4.8)
simplecov (>= 0.7.1, < 1.0.0)
coderay (1.1.0)
coffee-rails (4.0.1)
@@ -93,13 +93,12 @@ GEM
coffee-script (2.4.1)
coffee-script-source
execjs
- coffee-script-source (1.9.1)
- colorize (0.7.7)
+ coffee-script-source (1.10.0)
concurrent-ruby (1.0.0)
concurrent-ruby (1.0.0-java)
concurrent-ruby-ext (1.0.0)
concurrent-ruby (~> 1.0.0)
- database_cleaner (1.4.1)
+ database_cleaner (1.5.1)
debug_inspector (0.0.2)
diff-lcs (1.2.5)
docile (1.1.5)
@@ -107,34 +106,34 @@ GEM
excon (>= 0.38.0)
json
erubis (2.7.0)
- eventmachine (1.0.8)
- eventmachine (1.0.8-java)
+ eventmachine (1.0.9.1)
+ eventmachine (1.0.9.1-java)
excon (0.45.4)
- execjs (2.5.2)
+ execjs (2.6.0)
factory_girl (4.5.0)
activesupport (>= 3.0.0)
- factory_girl_rails (4.5.0)
+ factory_girl_rails (4.6.0)
factory_girl (~> 4.5.0)
railties (>= 3.0.0)
- faraday (0.9.1)
+ faraday (0.9.2)
multipart-post (>= 1.2, < 3)
- faye-websocket (0.10.0)
+ faye-websocket (0.10.2)
eventmachine (>= 0.12.0)
websocket-driver (>= 0.5.1)
- ffi (1.9.8)
- ffi (1.9.8-java)
+ ffi (1.9.10)
+ ffi (1.9.10-java)
forgery (0.6.0)
- highline (1.7.1)
+ highline (1.7.8)
hike (1.2.3)
i18n (0.7.0)
- ims-lti (1.1.8)
+ ims-lti (1.1.10)
builder
oauth (~> 0.4.5)
- jbuilder (2.2.13)
- activesupport (>= 3.0.0, < 5)
+ jbuilder (2.4.1)
+ activesupport (>= 3.0.0, < 5.1)
multi_json (~> 1.2)
- jdbc-postgres (9.4.1200)
- jquery-rails (3.1.2)
+ jdbc-postgres (9.4.1206)
+ jquery-rails (3.1.4)
railties (>= 3.0, < 5.0)
thor (>= 0.14, < 2.0)
jquery-turbolinks (2.1.0)
@@ -142,115 +141,117 @@ GEM
turbolinks
json (1.8.3)
json (1.8.3-java)
- jwt (1.4.1)
- kramdown (1.6.0)
+ jwt (1.5.1)
+ kramdown (1.9.0)
mail (2.6.3)
mime-types (>= 1.16, < 3)
method_source (0.8.2)
mime-types (2.99)
- mini_portile (0.6.2)
- minitest (5.8.3)
+ mini_portile2 (2.0.0)
+ minitest (5.8.4)
multi_json (1.11.2)
multi_xml (0.5.5)
multipart-post (2.0.0)
net-scp (1.2.1)
net-ssh (>= 2.6.5)
- net-ssh (2.9.2)
- newrelic_rpm (3.11.2.286)
- nokogiri (1.6.6.2)
- mini_portile (~> 0.6.0)
- nokogiri (1.6.6.2-java)
+ net-ssh (3.0.2)
+ newrelic_rpm (3.14.3.313)
+ nokogiri (1.6.7.2)
+ mini_portile2 (~> 2.0.0.rc2)
+ nokogiri (1.6.7.2-java)
nyan-cat-formatter (0.11)
rspec (>= 2.99, >= 2.14.2, < 4)
oauth (0.4.7)
- oauth2 (1.0.0)
+ oauth2 (1.1.0)
faraday (>= 0.8, < 0.10)
- jwt (~> 1.0)
+ jwt (~> 1.0, < 1.5.2)
multi_json (~> 1.3)
multi_xml (~> 0.5)
- rack (~> 1.2)
- parser (2.2.0.3)
- ast (>= 1.1, < 3.0)
- pg (0.18.1)
- polyamorous (1.2.0)
+ rack (>= 1.2, < 3)
+ parser (2.3.0.6)
+ ast (~> 2.2)
+ pg (0.18.4)
+ polyamorous (1.3.0)
activerecord (>= 3.0)
- powerpack (0.1.0)
- pry (0.10.1)
+ powerpack (0.1.1)
+ pry (0.10.3)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
- pry (0.10.1-java)
+ pry (0.10.3-java)
coderay (~> 1.1.0)
method_source (~> 0.8.1)
slop (~> 3.4)
spoon (~> 0.0)
puma (2.15.3)
puma (2.15.3-java)
- pundit (0.3.0)
+ pundit (1.1.0)
activesupport (>= 3.0.0)
rack (1.5.5)
rack-test (0.6.3)
rack (>= 1.0)
- rails (4.1.14)
- actionmailer (= 4.1.14)
- actionpack (= 4.1.14)
- actionview (= 4.1.14)
- activemodel (= 4.1.14)
- activerecord (= 4.1.14)
- activesupport (= 4.1.14)
+ rails (4.1.14.1)
+ actionmailer (= 4.1.14.1)
+ actionpack (= 4.1.14.1)
+ actionview (= 4.1.14.1)
+ activemodel (= 4.1.14.1)
+ activerecord (= 4.1.14.1)
+ activesupport (= 4.1.14.1)
bundler (>= 1.3.0, < 2.0)
- railties (= 4.1.14)
+ railties (= 4.1.14.1)
sprockets-rails (~> 2.0)
- rails-i18n (4.0.4)
- i18n (~> 0.6)
+ rails-i18n (4.0.8)
+ i18n (~> 0.7)
railties (~> 4.0)
- railties (4.1.14)
- actionpack (= 4.1.14)
- activesupport (= 4.1.14)
+ railties (4.1.14.1)
+ actionpack (= 4.1.14.1)
+ activesupport (= 4.1.14.1)
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
- rainbow (2.0.0)
- rake (10.4.2)
- ransack (1.6.6)
+ rainbow (2.1.0)
+ rake (10.5.0)
+ ransack (1.7.0)
actionpack (>= 3.0)
activerecord (>= 3.0)
activesupport (>= 3.0)
i18n
polyamorous (~> 1.2)
- rdoc (4.2.0)
- rspec (3.1.0)
- rspec-core (~> 3.1.0)
- rspec-expectations (~> 3.1.0)
- rspec-mocks (~> 3.1.0)
+ rdoc (4.2.2)
+ json (~> 1.4)
+ rspec (3.4.0)
+ rspec-core (~> 3.4.0)
+ rspec-expectations (~> 3.4.0)
+ rspec-mocks (~> 3.4.0)
rspec-autotest (1.0.0)
rspec-core (>= 2.99.0.beta1, < 4.0.0)
- rspec-core (3.1.7)
- rspec-support (~> 3.1.0)
- rspec-expectations (3.1.2)
+ rspec-core (3.4.2)
+ rspec-support (~> 3.4.0)
+ rspec-expectations (3.4.0)
diff-lcs (>= 1.2.0, < 2.0)
- rspec-support (~> 3.1.0)
- rspec-mocks (3.1.3)
- rspec-support (~> 3.1.0)
- rspec-rails (3.1.0)
- actionpack (>= 3.0)
- activesupport (>= 3.0)
- railties (>= 3.0)
- rspec-core (~> 3.1.0)
- rspec-expectations (~> 3.1.0)
- rspec-mocks (~> 3.1.0)
- rspec-support (~> 3.1.0)
- rspec-support (3.1.2)
- rubocop (0.30.0)
- astrolabe (~> 1.3)
- parser (>= 2.2.0.1, < 3.0)
+ rspec-support (~> 3.4.0)
+ rspec-mocks (3.4.1)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.4.0)
+ rspec-rails (3.4.2)
+ actionpack (>= 3.0, < 4.3)
+ activesupport (>= 3.0, < 4.3)
+ railties (>= 3.0, < 4.3)
+ rspec-core (~> 3.4.0)
+ rspec-expectations (~> 3.4.0)
+ rspec-mocks (~> 3.4.0)
+ rspec-support (~> 3.4.0)
+ rspec-support (3.4.1)
+ rubocop (0.37.2)
+ parser (>= 2.3.0.4, < 3.0)
powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0)
- ruby-progressbar (~> 1.4)
- rubocop-rspec (1.2.2)
+ ruby-progressbar (~> 1.7)
+ unicode-display_width (~> 0.3)
+ rubocop-rspec (1.4.0)
ruby-progressbar (1.7.5)
- rubytree (0.9.4)
+ rubytree (0.9.7)
json (~> 1.8)
- structured_warnings (~> 0.1)
+ structured_warnings (~> 0.2)
rubyzip (1.1.7)
sass (3.2.19)
sass-rails (4.0.5)
@@ -261,17 +262,17 @@ GEM
sdoc (0.4.1)
json (~> 1.7, >= 1.7.7)
rdoc (~> 4.0)
- selenium-webdriver (2.45.0)
+ selenium-webdriver (2.52.0)
childprocess (~> 0.5)
multi_json (~> 1.0)
rubyzip (~> 1.0)
websocket (~> 1.0)
- simplecov (0.9.2)
+ simplecov (0.11.2)
docile (~> 1.1.0)
- multi_json (~> 1.0)
- simplecov-html (~> 0.9.0)
- simplecov-html (0.9.0)
- slim (3.0.3)
+ json (~> 1.8)
+ simplecov-html (~> 0.10.0)
+ simplecov-html (0.10.0)
+ slim (3.0.6)
temple (~> 0.7.3)
tilt (>= 1.3.3, < 2.1)
slop (3.6.0)
@@ -281,7 +282,7 @@ GEM
oauth2 (>= 0.8.0)
spoon (0.0.4)
ffi
- spring (1.3.4)
+ spring (1.6.3)
sprockets (2.12.4)
hike (~> 1.2)
multi_json (~> 1.0)
@@ -291,12 +292,11 @@ GEM
actionpack (>= 3.0)
activesupport (>= 3.0)
sprockets (>= 2.8, < 4.0)
- sshkit (1.7.1)
- colorize (>= 0.7.0)
+ sshkit (1.8.1)
net-scp (>= 1.1.2)
net-ssh (>= 2.8.0)
structured_warnings (0.2.0)
- temple (0.7.5)
+ temple (0.7.6)
thor (0.19.1)
thread_safe (0.3.5)
thread_safe (0.3.5-java)
@@ -308,21 +308,22 @@ GEM
coffee-rails
tzinfo (1.2.2)
thread_safe (~> 0.1)
- uglifier (2.7.1)
+ uglifier (2.7.2)
execjs (>= 0.3.0)
json (>= 1.8.0)
- web-console (2.1.2)
+ unicode-display_width (0.3.1)
+ web-console (2.3.0)
activemodel (>= 4.0)
binding_of_caller (>= 0.7.2)
railties (>= 4.0)
sprockets-rails (>= 2.0, < 4.0)
- websocket (1.2.1)
- websocket-driver (0.6.2)
+ websocket (1.2.2)
+ websocket-driver (0.6.3)
websocket-extensions (>= 0.1.0)
- websocket-driver (0.6.2-java)
+ websocket-driver (0.6.3-java)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.2)
- will_paginate (3.0.7)
+ will_paginate (3.1.0)
xpath (2.0.0)
nokogiri (~> 1.3)
@@ -361,6 +362,7 @@ DEPENDENCIES
jquery-turbolinks
kramdown
newrelic_rpm
+ nokogiri
nyan-cat-formatter
pg
pry
diff --git a/app/assets/javascripts/editor.js.erb b/app/assets/javascripts/editor.js.erb
index b5eefbf5..2da4a37b 100644
--- a/app/assets/javascripts/editor.js.erb
+++ b/app/assets/javascripts/editor.js.erb
@@ -30,7 +30,7 @@ $(function() {
numMessages = 0,
turtlecanvas = $('#turtlecanvas'),
prompt = $('#prompt'),
- commands = ['input', 'write', 'turtle', 'turtlebatch', 'exit', 'timeout', 'status'],
+ commands = ['input', 'write', 'turtle', 'turtlebatch', 'render', 'exit', 'timeout', 'status'],
streams = ['stdin', 'stdout', 'stderr'];
var ENTER_KEY_CODE = 13;
@@ -245,6 +245,18 @@ $(function() {
}
};
+ var findOrCreateRenderElement = function(index) {
+ if ($('#render-' + index).isPresent()) {
+ return $('#render-' + index);
+ } else {
+ var element = $('
').attr('id', 'render-' + index);
+ $('#render').append(element);
+ return element;
+ }
+ };
+
+
+
var getPanelClass = function(result) {
if (result.stderr && !result.score) {
return 'panel-danger';
@@ -1172,6 +1184,9 @@ $(function() {
showCanvas();
handleTurtlebatchCommand(msg);
break;
+ case 'render':
+ renderWebsocketOutput(msg);
+ break;
case 'exit':
killWebsocketAndContainer();
break;
@@ -1185,6 +1200,11 @@ $(function() {
}
};
+ var renderWebsocketOutput = function(msg){
+ var element = findOrCreateRenderElement(0);
+ element.append(msg.data);
+ };
+
var printWebsocketOutput = function(msg) {
if (!msg.data) {
return;
diff --git a/app/assets/stylesheets/code_harbor_links.css.scss b/app/assets/stylesheets/code_harbor_links.css.scss
new file mode 100644
index 00000000..5ca7b3ce
--- /dev/null
+++ b/app/assets/stylesheets/code_harbor_links.css.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the CodeHarborLinks controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/app/assets/stylesheets/scaffolds.css.scss b/app/assets/stylesheets/scaffolds.css.scss
new file mode 100644
index 00000000..6ec6a8ff
--- /dev/null
+++ b/app/assets/stylesheets/scaffolds.css.scss
@@ -0,0 +1,69 @@
+body {
+ background-color: #fff;
+ color: #333;
+ font-family: verdana, arial, helvetica, sans-serif;
+ font-size: 13px;
+ line-height: 18px;
+}
+
+p, ol, ul, td {
+ font-family: verdana, arial, helvetica, sans-serif;
+ font-size: 13px;
+ line-height: 18px;
+}
+
+pre {
+ background-color: #eee;
+ padding: 10px;
+ font-size: 11px;
+}
+
+a {
+ color: #000;
+ &:visited {
+ color: #666;
+ }
+ &:hover {
+ color: #fff;
+ background-color: #000;
+ }
+}
+
+div {
+ &.field, &.actions {
+ margin-bottom: 10px;
+ }
+}
+
+#notice {
+ color: green;
+}
+
+.field_with_errors {
+ padding: 2px;
+ background-color: red;
+ display: table;
+}
+
+#error_explanation {
+ width: 450px;
+ border: 2px solid red;
+ padding: 7px;
+ padding-bottom: 0;
+ margin-bottom: 20px;
+ background-color: #f0f0f0;
+ h2 {
+ text-align: left;
+ font-weight: bold;
+ padding: 5px 5px 5px 15px;
+ font-size: 12px;
+ margin: -7px;
+ margin-bottom: 0px;
+ background-color: #c00;
+ color: #fff;
+ }
+ ul li {
+ font-size: 12px;
+ list-style: square;
+ }
+}
diff --git a/app/controllers/code_harbor_links_controller.rb b/app/controllers/code_harbor_links_controller.rb
new file mode 100644
index 00000000..a4736f26
--- /dev/null
+++ b/app/controllers/code_harbor_links_controller.rb
@@ -0,0 +1,68 @@
+class CodeHarborLinksController < ApplicationController
+ include CommonBehavior
+ before_action :set_code_harbor_link, only: [:show, :edit, :update, :destroy]
+
+ def authorize!
+ authorize(@code_harbor_link || @code_harbor_links)
+ end
+ private :authorize!
+
+ # GET /code_harbor_links
+ # GET /code_harbor_links.json
+ def index
+ @code_harbor_links = CodeHarborLink.where(user_id: current_user.id).paginate(page: params[:page])
+ authorize!
+ end
+
+ # GET /code_harbor_links/1
+ # GET /code_harbor_links/1.json
+ def show
+ authorize!
+ end
+
+ # GET /code_harbor_links/new
+ def new
+ @code_harbor_link = CodeHarborLink.new
+ authorize!
+ end
+
+ # GET /code_harbor_links/1/edit
+ def edit
+ authorize!
+ end
+
+ # POST /code_harbor_links
+ # POST /code_harbor_links.json
+ def create
+ @code_harbor_link = CodeHarborLink.new(code_harbor_link_params)
+ @code_harbor_link.user = current_user
+ authorize!
+ create_and_respond(object: @code_harbor_link)
+ end
+
+ # PATCH/PUT /code_harbor_links/1
+ # PATCH/PUT /code_harbor_links/1.json
+ def update
+ update_and_respond(object: @code_harbor_link, params: code_harbor_link_params)
+ authorize!
+ end
+
+ # DELETE /code_harbor_links/1
+ # DELETE /code_harbor_links/1.json
+ def destroy
+ destroy_and_respond(object: @code_harbor_link)
+ end
+
+ private
+ # Use callbacks to share common setup or constraints between actions.
+ def set_code_harbor_link
+ @code_harbor_link = CodeHarborLink.find(params[:id])
+ @code_harbor_link.user = current_user
+ authorize!
+ end
+
+ # Never trust parameters from the scary internet, only allow the white list through.
+ def code_harbor_link_params
+ params.require(:code_harbor_link).permit(:oauth2token)
+ end
+end
diff --git a/app/controllers/exercises_controller.rb b/app/controllers/exercises_controller.rb
index cfe4d155..69d1f46f 100644
--- a/app/controllers/exercises_controller.rb
+++ b/app/controllers/exercises_controller.rb
@@ -11,6 +11,10 @@ class ExercisesController < ApplicationController
before_action :set_file_types, only: [:create, :edit, :new, :update]
before_action :set_teams, only: [:create, :edit, :new, :update]
+ skip_before_filter :verify_authenticity_token, only: [:import_proforma_xml]
+ skip_after_action :verify_authorized, only: [:import_proforma_xml]
+ skip_after_action :verify_policy_scoped, only: [:import_proforma_xml]
+
def authorize!
authorize(@exercise || @exercises)
end
@@ -62,6 +66,58 @@ class ExercisesController < ApplicationController
def edit
end
+ def import_proforma_xml
+ begin
+ user = user_for_oauth2_request()
+ exercise = Exercise.new
+ request_body = request.body.read
+ exercise.from_proforma_xml(request_body)
+ exercise.user = user
+ saved = exercise.save
+ if saved
+ render :text => 'SUCCESS', :status => 200
+ else
+ logger.info(exercise.errors.full_messages)
+ render :text => 'Invalid exercise', :status => 400
+ end
+ rescue => error
+ if error.class == Hash
+ render :text => error.message, :status => error.status
+ else
+ raise error
+ render :text => '', :status => 500
+ end
+ end
+ end
+
+ def user_for_oauth2_request
+ authorizationHeader = request.headers['Authorization']
+ if authorizationHeader == nil
+ raise ({status: 401, message: 'No Authorization header'})
+ end
+
+ oauth2Token = authorizationHeader.split(' ')[1]
+ if oauth2Token == nil || oauth2Token.size == 0
+ raise ({status: 401, message: 'No token in Authorization header'})
+ end
+
+ user = user_by_code_harbor_token(oauth2Token)
+ if user == nil
+ raise ({status: 401, message: 'Unknown OAuth2 token'})
+ end
+
+ return user
+ end
+ private :user_for_oauth2_request
+
+ def user_by_code_harbor_token(oauth2Token)
+ link = CodeHarborLink.where(:oauth2token => oauth2Token)[0]
+ if link != nil
+ return link.user
+ end
+ end
+ private :user_by_code_harbor_token
+
def exercise_params
params[:exercise].permit(:description, :execution_environment_id, :file_id, :instructions, :public, :hide_file_tree, :team_id, :title, files_attributes: file_attributes).merge(user_id: current_user.id, user_type: current_user.class.name)
end
diff --git a/app/controllers/submissions_controller.rb b/app/controllers/submissions_controller.rb
index 3aa3adf4..8f26d986 100644
--- a/app/controllers/submissions_controller.rb
+++ b/app/controllers/submissions_controller.rb
@@ -111,6 +111,7 @@ class SubmissionsController < ApplicationController
if result[:status] == :container_running
socket = result[:socket]
+ command = result[:command]
socket.on :message do |event|
Rails.logger.info( Time.now.getutc.to_s + ": Docker sending: " + event.data)
@@ -139,6 +140,11 @@ class SubmissionsController < ApplicationController
Rails.logger.debug('Rescued parsing error, sent the received client data to docker:' + data)
end
end
+
+ # Send command after all listeners are attached.
+ # Newline required to flush
+ socket.send command + "\n"
+ Rails.logger.info('Sent command: ' + command.to_s)
else
kill_socket(tubesock)
end
@@ -176,7 +182,24 @@ class SubmissionsController < ApplicationController
for part in message.split("\n")
self.parse_message(part,output_stream,socket,false)
end
+ elsif(message.include? "
![]()
"))
+ @buffer += message
+ parsed = {'cmd'=>'write','stream'=>output_stream,'data'=>@buffer}
+ socket.send_data JSON.dump(parsed)
+ #socket.send_data @buffer
+ @buffering = false
+ #Rails.logger.info('Sent complete buffer')
+ elsif(@buffering)
+ @buffer += message
+ #Rails.logger.info('Appending to buffer')
else
+ #Rails.logger.info('else')
parsed = {'cmd'=>'write','stream'=>output_stream,'data'=>message}
socket.send_data JSON.dump(parsed)
Rails.logger.info('parse_message sent: ' + JSON.dump(parsed))
diff --git a/app/helpers/code_harbor_links_helper.rb b/app/helpers/code_harbor_links_helper.rb
new file mode 100644
index 00000000..d8e92ddf
--- /dev/null
+++ b/app/helpers/code_harbor_links_helper.rb
@@ -0,0 +1,2 @@
+module CodeHarborLinksHelper
+end
diff --git a/app/models/code_harbor_link.rb b/app/models/code_harbor_link.rb
new file mode 100644
index 00000000..2e219aa9
--- /dev/null
+++ b/app/models/code_harbor_link.rb
@@ -0,0 +1,13 @@
+class CodeHarborLink < ActiveRecord::Base
+ validates :oauth2token, presence: true
+ validates :user_id, presence: true
+
+ belongs_to :internal_user, foreign_key: :user_id
+ alias_method :user, :internal_user
+ alias_method :user=, :internal_user=
+
+ def to_s
+ oauth2token
+ end
+
+end
diff --git a/app/models/exercise.rb b/app/models/exercise.rb
index 658a8fb5..4a1e0486 100644
--- a/app/models/exercise.rb
+++ b/app/models/exercise.rb
@@ -1,3 +1,4 @@
+require 'nokogiri'
require File.expand_path('../../../lib/active_model/validations/boolean_presence_validator', __FILE__)
class Exercise < ActiveRecord::Base
@@ -29,7 +30,11 @@ class Exercise < ActiveRecord::Base
def average_percentage
- (average_score / maximum_score * 100).round if average_score and maximum_score != 0.0 else 0
+ if average_score and maximum_score != 0.0
+ (average_score / maximum_score * 100).round
+ else
+ 0
+ end
end
def average_score
@@ -105,6 +110,54 @@ class Exercise < ActiveRecord::Base
exercise
end
+ def determine_file_role_from_proforma_file(task_node, file_node)
+ file_id = file_node.xpath('@id')
+ file_class = file_node.xpath('@class').first.value
+ comment = file_node.xpath('@comment').first.value
+ is_referenced_by_test = task_node.xpath("p:tests/p:test/p:filerefs/p:fileref[@id=#{file_id}]")
+ is_referenced_by_model_solution = task_node.xpath("p:model-solutions/p:model-solution/p:filerefs/p:fileref[@id=#{file_id}]")
+ if is_referenced_by_test && (file_class == 'internal')
+ return 'teacher_defined_test'
+ elsif is_referenced_by_model_solution && (file_class == 'internal')
+ return 'reference_implementation'
+ elsif (file_class == 'template') && (comment == 'main')
+ return 'main_file'
+ elsif (file_class == 'internal') && (comment == 'main')
+ end
+ return 'regular_file'
+ end
+
+ def from_proforma_xml(xml_string)
+ # how to extract the proforma functionality into a different module in rails?
+ xml = Nokogiri::XML(xml_string)
+ xml.collect_namespaces
+ task_node = xml.xpath('/root/p:task')
+ description = task_node.xpath('p:description/text()')[0].content
+ self.attributes = {
+ title: task_node.xpath('p:meta-data/p:title/text()')[0].content,
+ description: description,
+ instructions: description
+ }
+ task_node.xpath('p:files/p:file').all? { |file|
+ file_name_split = file.xpath('@filename').first.value.split('.')
+ file_class = file.xpath('@class').first.value
+ role = determine_file_role_from_proforma_file(task_node, file)
+ feedback_message_nodes = task_node.xpath("p:tests/p:test/p:test-configuration/c:feedback-message/text()")
+ files.build({
+ name: file_name_split.first,
+ content: file.xpath('text()').first.content,
+ read_only: false,
+ hidden: file_class == 'internal',
+ role: role,
+ feedback_message: (role == 'teacher_defined_test') ? feedback_message_nodes.first.content : nil,
+ file_type: FileType.where(
+ file_extension: ".#{file_name_split.second}"
+ ).take
+ })
+ }
+ self.execution_environment_id = 1
+ end
+
def generate_token
self.token ||= SecureRandom.hex(4)
end
@@ -129,4 +182,5 @@ class Exercise < ActiveRecord::Base
end
end
private :valid_main_file?
+
end
diff --git a/app/policies/code_harbor_link_policy.rb b/app/policies/code_harbor_link_policy.rb
new file mode 100644
index 00000000..8726c22a
--- /dev/null
+++ b/app/policies/code_harbor_link_policy.rb
@@ -0,0 +1,3 @@
+class CodeHarborLinkPolicy < AdminOnlyPolicy
+
+end
diff --git a/app/views/application/_navigation.html.slim b/app/views/application/_navigation.html.slim
index 276723bf..4ab39e30 100644
--- a/app/views/application/_navigation.html.slim
+++ b/app/views/application/_navigation.html.slim
@@ -8,7 +8,7 @@
- if current_user.admin?
li = link_to(t('breadcrumbs.dashboard.show'), admin_dashboard_path)
li.divider
- - models = [ExecutionEnvironment, Exercise, Consumer, ExternalUser, FileType, InternalUser, Submission, Team].sort_by { |model| model.model_name.human(count: 2) }
+ - models = [ExecutionEnvironment, Exercise, Consumer, CodeHarborLink, ExternalUser, FileType, InternalUser, Submission, Team].sort_by { |model| model.model_name.human(count: 2) }
- models.each do |model|
- if policy(model).index?
li = link_to(model.model_name.human(count: 2), send(:"#{model.model_name.collection}_path"))
diff --git a/app/views/code_harbor_links/_form.html.slim b/app/views/code_harbor_links/_form.html.slim
new file mode 100644
index 00000000..f7b449ee
--- /dev/null
+++ b/app/views/code_harbor_links/_form.html.slim
@@ -0,0 +1,6 @@
+= form_for(@code_harbor_link) do |f|
+ = render('shared/form_errors', object: @code_harbor_link)
+ .form-group
+ = f.label(:oauth2token)
+ = f.text_field(:oauth2token, class: 'form-control', required: true)
+ .actions = render('shared/submit_button', f: f, object: @code_harbor_link)
diff --git a/app/views/code_harbor_links/edit.html.slim b/app/views/code_harbor_links/edit.html.slim
new file mode 100644
index 00000000..d1c7ea8f
--- /dev/null
+++ b/app/views/code_harbor_links/edit.html.slim
@@ -0,0 +1,3 @@
+h1 = @code_harbor_link
+
+= render('form')
diff --git a/app/views/code_harbor_links/index.html.slim b/app/views/code_harbor_links/index.html.slim
new file mode 100644
index 00000000..953985c4
--- /dev/null
+++ b/app/views/code_harbor_links/index.html.slim
@@ -0,0 +1,18 @@
+h1 = CodeHarborLink.model_name.human(count: 2)
+
+.table-responsive
+ table.table
+ thead
+ tr
+ th = t('activerecord.attributes.code_harbor_link.oauth2token')
+ th colspan=3 = t('shared.actions')
+ tbody
+ - @code_harbor_links.each do |code_harbor_link|
+ tr
+ td = code_harbor_link.oauth2token
+ td = link_to(t('shared.show'), code_harbor_link)
+ td = link_to(t('shared.edit'), edit_code_harbor_link_path(code_harbor_link))
+ td = link_to(t('shared.destroy'), code_harbor_link, data: {confirm: t('shared.confirm_destroy')}, method: :delete)
+
+= render('shared/pagination', collection: @code_harbor_links)
+p = render('shared/new_button', model: CodeHarborLink)
diff --git a/app/views/code_harbor_links/new.html.slim b/app/views/code_harbor_links/new.html.slim
new file mode 100644
index 00000000..ef19a3e6
--- /dev/null
+++ b/app/views/code_harbor_links/new.html.slim
@@ -0,0 +1,3 @@
+h1 = t('shared.new_model', model: CodeHarborLink.model_name.human)
+
+= render('form')
diff --git a/app/views/code_harbor_links/show.html.slim b/app/views/code_harbor_links/show.html.slim
new file mode 100644
index 00000000..b2d95342
--- /dev/null
+++ b/app/views/code_harbor_links/show.html.slim
@@ -0,0 +1,7 @@
+h1
+ = @code_harbor_link
+ = render('shared/edit_button', object: @code_harbor_link) if policy(@code_harbor_link).edit?
+
+- %w[oauth2token].each do |attribute|
+ = row(label: "code_harbor_link.#{attribute}") do
+ = content_tag(:input, nil, class: 'form-control', readonly: true, value: @code_harbor_link.send(attribute))
diff --git a/config/locales/de.yml b/config/locales/de.yml
index 91898861..5d0bc8b1 100644
--- a/config/locales/de.yml
+++ b/config/locales/de.yml
@@ -1,6 +1,8 @@
de:
activerecord:
attributes:
+ code_harbor_link:
+ oauth2token: OAuth2 Token
consumer:
name: Name
oauth_key: OAuth Key
@@ -90,6 +92,9 @@ de:
internal_user_ids: Mitglieder
name: Name
models:
+ code_harbor_link:
+ one: CodeHarbor-Link
+ other: CodeHarbor-Links
consumer:
one: Konsument
other: Konsumenten
diff --git a/config/locales/en.yml b/config/locales/en.yml
index a4e7b616..38c556f5 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -1,6 +1,8 @@
en:
activerecord:
attributes:
+ code_harbor_link:
+ oauth2token: OAuth2 Token
consumer:
name: Name
oauth_key: OAuth Key
@@ -90,6 +92,9 @@ en:
internal_user_ids: Members
name: Name
models:
+ code_harbor_link:
+ one: CodeHarbor Link
+ other: CodeHarbor Links
consumer:
one: Consumer
other: Consumers
diff --git a/config/routes.rb b/config/routes.rb
index 24f6a0d9..72ccb489 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,6 +1,7 @@
FILENAME_REGEXP = /[\w\.]+/ unless Kernel.const_defined?(:FILENAME_REGEXP)
Rails.application.routes.draw do
+ resources :code_harbor_links
resources :request_for_comments
get '/my_request_for_comments', as: 'my_request_for_comments', to: 'request_for_comments#get_my_comment_requests'
resources :comments, except: [:destroy] do
@@ -40,6 +41,8 @@ Rails.application.routes.draw do
resources :hints
end
+ post '/import_proforma_xml' => 'exercises#import_proforma_xml'
+
resources :exercises do
collection do
match '', to: 'exercises#batch_update', via: [:patch, :put]
diff --git a/db/migrate/20160204094409_create_code_harbor_links.rb b/db/migrate/20160204094409_create_code_harbor_links.rb
new file mode 100644
index 00000000..b87d5c9d
--- /dev/null
+++ b/db/migrate/20160204094409_create_code_harbor_links.rb
@@ -0,0 +1,9 @@
+class CreateCodeHarborLinks < ActiveRecord::Migration
+ def change
+ create_table :code_harbor_links do |t|
+ t.string :oauth2token
+
+ t.timestamps
+ end
+ end
+end
diff --git a/db/migrate/20160204111716_add_user_to_code_harbor_link.rb b/db/migrate/20160204111716_add_user_to_code_harbor_link.rb
new file mode 100644
index 00000000..8fa36ed1
--- /dev/null
+++ b/db/migrate/20160204111716_add_user_to_code_harbor_link.rb
@@ -0,0 +1,5 @@
+class AddUserToCodeHarborLink < ActiveRecord::Migration
+ def change
+ add_reference :code_harbor_links, :user, index: true, foreign_key: true
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index d895da3f..793c021a 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,11 +11,20 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20150922125415) do
+ActiveRecord::Schema.define(version: 20160204111716) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
+ create_table "code_harbor_links", force: true do |t|
+ t.string "oauth2token"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ t.integer "user_id"
+ end
+
+ add_index "code_harbor_links", ["user_id"], name: "index_code_harbor_links_on_user_id", using: :btree
+
create_table "comments", force: true do |t|
t.integer "user_id"
t.integer "file_id"
diff --git a/lib/docker_client.rb b/lib/docker_client.rb
index fc760981..80986377 100644
--- a/lib/docker_client.rb
+++ b/lib/docker_client.rb
@@ -21,14 +21,14 @@ class DockerClient
end
def self.clean_container_workspace(container)
- container.exec(['bash', '-c', 'rm -rf ' + CONTAINER_WORKSPACE_PATH + '/*'])
-=begin
+ # remove files when using transferral via Docker API archive_in (transmit)
+ #container.exec(['bash', '-c', 'rm -rf ' + CONTAINER_WORKSPACE_PATH + '/*'])
+
local_workspace_path = local_workspace_path(container)
if local_workspace_path && Pathname.new(local_workspace_path).exist?
Pathname.new(local_workspace_path).children.each{ |p| p.rmtree}
#FileUtils.rmdir(Pathname.new(local_workspace_path))
end
-=end
end
def command_substitutions(filename)
@@ -96,9 +96,10 @@ class DockerClient
#Rails.logger.info "docker_client: self.create_container with creation options:"
#Rails.logger.info(container_creation_options(execution_environment))
container = Docker::Container.create(container_creation_options(execution_environment))
+ # container.start sometimes creates the passed local_workspace_path on disk (depending on the setup).
+ # this is however not guaranteed and caused issues on the server already. Therefore create the necessary folders manually!
local_workspace_path = generate_local_workspace_path
- # container.start always creates the passed local_workspace_path on disk. Seems like we have to live with that, therefore we can also just create the empty folder ourselves.
- # FileUtils.mkdir(local_workspace_path)
+ FileUtils.mkdir(local_workspace_path)
container.start(container_start_options(execution_environment, local_workspace_path))
container.start_time = Time.now
container.status = :created
@@ -217,7 +218,7 @@ class DockerClient
end
#called when the user clicks the "Run" button
- def execute_websocket_command(command, before_execution_block, output_consuming_block)
+ def open_websocket_connection(command, before_execution_block, output_consuming_block)
@container = DockerContainerPool.get_container(@execution_environment)
if @container
@container.status = :executing
@@ -230,10 +231,7 @@ class DockerClient
end
# TODO: catch exception if socket could not be created
@socket ||= create_socket(@container)
- # Newline required to flush
- @socket.send command + "\n"
- Rails.logger.info('Sent command: ' + command.to_s)
- {status: :container_running, socket: @socket, container: @container}
+ {status: :container_running, socket: @socket, container: @container, command: command}
else
{status: :container_depleted}
end
@@ -295,8 +293,9 @@ class DockerClient
Run commands by attaching a websocket to Docker.
"""
command = submission.execution_environment.run_command % command_substitutions(filename)
- create_workspace_files = proc { create_workspace_files_transmit(container, submission) }
- execute_websocket_command(command, create_workspace_files, block)
+ create_workspace_files = proc { create_workspace_files(container, submission) }
+ open_websocket_connection(command, create_workspace_files, block)
+ # actual run command is run in the submissions controller, after all listeners are attached.
end
def execute_test_command(submission, filename, &block)
@@ -304,7 +303,7 @@ class DockerClient
Stick to existing Docker API with exec command.
"""
command = submission.execution_environment.test_command % command_substitutions(filename)
- create_workspace_files = proc { create_workspace_files_transmit(container, submission) }
+ create_workspace_files = proc { create_workspace_files(container, submission) }
execute_command(command, create_workspace_files, block)
end
diff --git a/lib/docker_container_pool.rb b/lib/docker_container_pool.rb
index 90357875..7f1f16fd 100644
--- a/lib/docker_container_pool.rb
+++ b/lib/docker_container_pool.rb
@@ -41,16 +41,17 @@ class DockerContainerPool
@all_containers[execution_environment.id]+=[container]
if(!@containers[execution_environment.id].include?(container))
@containers[execution_environment.id]+=[container]
- Rails.logger.debug('Added container ' + container.to_s + ' to all_pool for execution environment ' + execution_environment.to_s + '. Containers in all_pool: ' + @all_containers[execution_environment.id].size.to_s)
+ #Rails.logger.debug('Added container ' + container.to_s + ' to all_pool for execution environment ' + execution_environment.to_s + '. Containers in all_pool: ' + @all_containers[execution_environment.id].size.to_s)
else
Rails.logger.info('failed trying to add existing container ' + container.to_s + ' to execution_environment ' + execution_environment.to_s)
end
end
def self.create_container(execution_environment)
+ Rails.logger.info('trying to create container for execution environment: ' + execution_environment.to_s)
container = DockerClient.create_container(execution_environment)
container.status = 'available'
- Rails.logger.debug('created container ' + container.to_s + ' for execution environment ' + execution_environment.to_s)
+ #Rails.logger.debug('created container ' + container.to_s + ' for execution environment ' + execution_environment.to_s)
container
end
@@ -120,11 +121,11 @@ class DockerContainerPool
if refill_count > 0
Rails.logger.info('Adding ' + refill_count.to_s + ' containers for execution_environment ' + execution_environment.name )
c = refill_count.times.map { create_container(execution_environment) }
- Rails.logger.info('Created containers: ' + c.to_s )
+ #Rails.logger.info('Created containers: ' + c.to_s )
@containers[execution_environment.id] += c
@all_containers[execution_environment.id] += c
- Rails.logger.debug('@containers for ' + execution_environment.name.to_s + ' (' + @containers.object_id.to_s + ') has the following content: '+ @containers[execution_environment.id].to_s)
- Rails.logger.debug('@all_containers for ' + execution_environment.name.to_s + ' (' + @all_containers.object_id.to_s + ') has the following content: ' + @all_containers[execution_environment.id].to_s)
+ #Rails.logger.debug('@containers for ' + execution_environment.name.to_s + ' (' + @containers.object_id.to_s + ') has the following content: '+ @containers[execution_environment.id].to_s)
+ #Rails.logger.debug('@all_containers for ' + execution_environment.name.to_s + ' (' + @all_containers.object_id.to_s + ') has the following content: ' + @all_containers[execution_environment.id].to_s)
end
end
diff --git a/lib/xikolo/client.rb b/lib/xikolo/client.rb
index db48c91a..ecad50e7 100644
--- a/lib/xikolo/client.rb
+++ b/lib/xikolo/client.rb
@@ -34,6 +34,7 @@ class Xikolo::Client
end
def self.url
+ #todo: JanR: set an environment variable here, fallback value: http://open.hpi.de/api/
'http://localhost:2000/api/'
end