coursemology-evaluator 0.1.1 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/.env +3 -3
  3. data/.gitignore +23 -22
  4. data/.hound.yml +8 -8
  5. data/.idea/Coursemology Evaluator.iml +8 -8
  6. data/.rspec +2 -2
  7. data/.rubocop.unhound.yml +109 -109
  8. data/.rubocop.yml +46 -46
  9. data/.travis.yml +17 -17
  10. data/Gemfile +4 -4
  11. data/Procfile +1 -1
  12. data/README.md +29 -29
  13. data/Rakefile +6 -6
  14. data/bin/evaluator +5 -5
  15. data/coursemology-evaluator.gemspec +37 -37
  16. data/lib/coursemology/evaluator.rb +36 -36
  17. data/lib/coursemology/evaluator/cli.rb +52 -52
  18. data/lib/coursemology/evaluator/client.rb +81 -75
  19. data/lib/coursemology/evaluator/docker_container.rb +59 -59
  20. data/lib/coursemology/evaluator/logging.rb +12 -12
  21. data/lib/coursemology/evaluator/logging/client_log_subscriber.rb +25 -25
  22. data/lib/coursemology/evaluator/logging/docker_log_subscriber.rb +18 -18
  23. data/lib/coursemology/evaluator/models.rb +7 -7
  24. data/lib/coursemology/evaluator/models/base.rb +50 -50
  25. data/lib/coursemology/evaluator/models/programming_evaluation.rb +55 -67
  26. data/lib/coursemology/evaluator/models/programming_evaluation/package.rb +12 -12
  27. data/lib/coursemology/evaluator/services.rb +6 -6
  28. data/lib/coursemology/evaluator/services/evaluate_programming_package_service.rb +151 -151
  29. data/lib/coursemology/evaluator/string_io.rb +14 -14
  30. data/lib/coursemology/evaluator/utils.rb +42 -42
  31. data/lib/coursemology/evaluator/version.rb +5 -5
  32. data/lib/coursemology/polyglot/extensions.rb +3 -3
  33. data/lib/coursemology/polyglot/extensions/language.rb +24 -24
  34. metadata +15 -4
  35. data/Gemfile.lock +0 -114
@@ -1,59 +1,59 @@
1
- # frozen_string_literal: true
2
- class Coursemology::Evaluator::DockerContainer < Docker::Container
3
- class << self
4
- def create(image, argv: nil)
5
- pull_image(image)
6
-
7
- ActiveSupport::Notifications.instrument('create.docker.evaluator.coursemology',
8
- image: image) do |payload|
9
- options = { 'Image' => image }
10
- options['Cmd'] = argv if argv && !argv.empty?
11
-
12
- payload[:container] = super(options)
13
- end
14
- end
15
-
16
- private
17
-
18
- def pull_image(image)
19
- ActiveSupport::Notifications.instrument('pull.docker.evaluator.coursemology',
20
- image: image) do
21
- Docker::Image.create('fromImage' => image)
22
- end
23
- end
24
- end
25
-
26
- # Waits for the container to exit the Running state.
27
- #
28
- # This will time out for long running operations, so keep retrying until we return.
29
- #
30
- # @param [Fixnum|nil] time The amount of time to wait.
31
- # @return [Fixnum] The exit code of the container.
32
- def wait(time = nil)
33
- container_state = info
34
- while container_state.fetch('State', {}).fetch('Running', true)
35
- super
36
- refresh!
37
- container_state = info
38
- end
39
-
40
- container_state['State']['ExitCode']
41
- rescue Docker::Error::TimeoutError
42
- retry
43
- end
44
-
45
- # Gets the exit code of the container.
46
- #
47
- # @return [Fixnum] The exit code of the container, if +wait+ was called before.
48
- # @return [nil] If the container is still running, or +wait+ was not called.
49
- def exit_code
50
- info.fetch('State', {})['ExitCode']
51
- end
52
-
53
- def delete
54
- ActiveSupport::Notifications.instrument('destroy.docker.evaluator.coursemology',
55
- container: id) do
56
- super
57
- end
58
- end
59
- end
1
+ # frozen_string_literal: true
2
+ class Coursemology::Evaluator::DockerContainer < Docker::Container
3
+ class << self
4
+ def create(image, argv: nil)
5
+ pull_image(image)
6
+
7
+ ActiveSupport::Notifications.instrument('create.docker.evaluator.coursemology',
8
+ image: image) do |payload|
9
+ options = { 'Image' => image }
10
+ options['Cmd'] = argv if argv && !argv.empty?
11
+
12
+ payload[:container] = super(options)
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def pull_image(image)
19
+ ActiveSupport::Notifications.instrument('pull.docker.evaluator.coursemology',
20
+ image: image) do
21
+ Docker::Image.create('fromImage' => image)
22
+ end
23
+ end
24
+ end
25
+
26
+ # Waits for the container to exit the Running state.
27
+ #
28
+ # This will time out for long running operations, so keep retrying until we return.
29
+ #
30
+ # @param [Fixnum|nil] time The amount of time to wait.
31
+ # @return [Fixnum] The exit code of the container.
32
+ def wait(time = nil)
33
+ container_state = info
34
+ while container_state.fetch('State', {}).fetch('Running', true)
35
+ super
36
+ refresh!
37
+ container_state = info
38
+ end
39
+
40
+ container_state['State']['ExitCode']
41
+ rescue Docker::Error::TimeoutError
42
+ retry
43
+ end
44
+
45
+ # Gets the exit code of the container.
46
+ #
47
+ # @return [Fixnum] The exit code of the container, if +wait+ was called before.
48
+ # @return [nil] If the container is still running, or +wait+ was not called.
49
+ def exit_code
50
+ info.fetch('State', {})['ExitCode']
51
+ end
52
+
53
+ def delete
54
+ ActiveSupport::Notifications.instrument('destroy.docker.evaluator.coursemology',
55
+ container: id) do
56
+ super
57
+ end
58
+ end
59
+ end
@@ -1,12 +1,12 @@
1
- # frozen_string_literal: true
2
- module Coursemology::Evaluator::Logging
3
- extend ActiveSupport::Autoload
4
-
5
- eager_autoload do
6
- autoload :ClientLogSubscriber
7
- autoload :DockerLogSubscriber
8
- end
9
- end
10
-
11
- # Define +Rails+ to trick ActiveSupport into logging to our logger.
12
- Rails = Coursemology::Evaluator
1
+ # frozen_string_literal: true
2
+ module Coursemology::Evaluator::Logging
3
+ extend ActiveSupport::Autoload
4
+
5
+ eager_autoload do
6
+ autoload :ClientLogSubscriber
7
+ autoload :DockerLogSubscriber
8
+ end
9
+ end
10
+
11
+ # Define +Rails+ to trick ActiveSupport into logging to our logger.
12
+ Rails = Coursemology::Evaluator
@@ -1,25 +1,25 @@
1
- # frozen_string_literal: true
2
- class Coursemology::Evaluator::Logging::ClientLogSubscriber < ActiveSupport::LogSubscriber
3
- def publish(name, *args)
4
- send(name.split('.').first, *args)
5
- end
6
-
7
- def allocate(event)
8
- info color("Client: Allocate (#{event.duration.round(1)}ms)", MAGENTA)
9
- end
10
-
11
- def allocate_fail(e:)
12
- error color("Client: Allocate failed: #{e.message}", RED)
13
- end
14
-
15
- def evaluate(event)
16
- info "#{color("Client: Evaluate (#{event.duration.round(1)}ms)", CYAN)} "\
17
- "#{event.payload[:evaluation].language.class.display_name}"
18
- end
19
-
20
- def save(event)
21
- info color("Client: Save (#{event.duration.round(1)}ms)", GREEN)
22
- end
23
- end
24
-
25
- Coursemology::Evaluator::Logging::ClientLogSubscriber.attach_to(:'client.evaluator.coursemology')
1
+ # frozen_string_literal: true
2
+ class Coursemology::Evaluator::Logging::ClientLogSubscriber < ActiveSupport::LogSubscriber
3
+ def publish(name, *args)
4
+ send(name.split('.').first, *args)
5
+ end
6
+
7
+ def allocate(event)
8
+ info color("Client: Allocate (#{event.duration.round(1)}ms)", MAGENTA)
9
+ end
10
+
11
+ def allocate_fail(e:)
12
+ error color("Client: Allocate failed: #{e.message}", RED)
13
+ end
14
+
15
+ def evaluate(event)
16
+ info "#{color("Client: Evaluate (#{event.duration.round(1)}ms)", CYAN)} "\
17
+ "#{event.payload[:evaluation].language.class.display_name}"
18
+ end
19
+
20
+ def save(event)
21
+ info color("Client: Save (#{event.duration.round(1)}ms)", GREEN)
22
+ end
23
+ end
24
+
25
+ Coursemology::Evaluator::Logging::ClientLogSubscriber.attach_to(:'client.evaluator.coursemology')
@@ -1,18 +1,18 @@
1
- # frozen_string_literal: true
2
- class Coursemology::Evaluator::Logging::DockerLogSubscriber < ActiveSupport::LogSubscriber
3
- def pull(event)
4
- info "#{color("Docker Pull (#{event.duration.round(1)}ms)", GREEN)} #{event.payload[:image]}"
5
- end
6
-
7
- def create(event)
8
- info "#{color("Docker Create (#{event.duration.round(1)}ms)", MAGENTA)} "\
9
- "#{event.payload[:image]} => #{event.payload[:container].id}"
10
- end
11
-
12
- def destroy(event)
13
- info "#{color("Docker Destroy (#{event.duration.round(1)}ms)", CYAN)} "\
14
- "#{event.payload[:container]}"
15
- end
16
- end
17
-
18
- Coursemology::Evaluator::Logging::DockerLogSubscriber.attach_to(:'docker.evaluator.coursemology')
1
+ # frozen_string_literal: true
2
+ class Coursemology::Evaluator::Logging::DockerLogSubscriber < ActiveSupport::LogSubscriber
3
+ def pull(event)
4
+ info "#{color("Docker Pull (#{event.duration.round(1)}ms)", GREEN)} #{event.payload[:image]}"
5
+ end
6
+
7
+ def create(event)
8
+ info "#{color("Docker Create (#{event.duration.round(1)}ms)", MAGENTA)} "\
9
+ "#{event.payload[:image]} => #{event.payload[:container].id}"
10
+ end
11
+
12
+ def destroy(event)
13
+ info "#{color("Docker Destroy (#{event.duration.round(1)}ms)", CYAN)} "\
14
+ "#{event.payload[:container]}"
15
+ end
16
+ end
17
+
18
+ Coursemology::Evaluator::Logging::DockerLogSubscriber.attach_to(:'docker.evaluator.coursemology')
@@ -1,7 +1,7 @@
1
- # frozen_string_literal: true
2
- module Coursemology::Evaluator::Models
3
- extend ActiveSupport::Autoload
4
-
5
- autoload :Base
6
- autoload :ProgrammingEvaluation
7
- end
1
+ # frozen_string_literal: true
2
+ module Coursemology::Evaluator::Models
3
+ extend ActiveSupport::Autoload
4
+
5
+ autoload :Base
6
+ autoload :ProgrammingEvaluation
7
+ end
@@ -1,50 +1,50 @@
1
- # frozen_string_literal: true
2
- class Coursemology::Evaluator::Models::Base < Flexirest::Base
3
- class << self
4
- attr_accessor :api_user_email
5
- attr_accessor :api_token
6
-
7
- def initialize
8
- Flexirest::Base.perform_caching = false
9
- default_config = Flexirest::Base.faraday_config
10
- Flexirest::Base.faraday_config do |faraday|
11
- # +follow_redirects+ must be added before declaring the adapter. See faraday_middleware#32,
12
- # last comment.
13
- faraday.response :follow_redirects
14
-
15
- default_config.call(faraday)
16
- end
17
- end
18
- end
19
-
20
- verbose!
21
- before_request :add_authentication
22
-
23
- # Sets the key of the model. This is the key that all attributes are nested under, the same as
24
- # the +require+ directive in the controller of the web application.
25
- #
26
- # @param [String] key The key to prefix all attributes with.
27
- def self.model_key(key)
28
- before_request do |name, param|
29
- fix_put_parameters(key, name, param) if [:post, :patch, :put].include?(param.method[:method])
30
- end
31
- end
32
- private_class_method :model_key
33
-
34
- # Fixes the request parameters when executing a POST, PATCH or PUT.
35
- #
36
- # @param [String] key The key to prefix all attributes with.
37
- # @param [Request] param The request parameter to prepend the key with.
38
- def self.fix_put_parameters(key, _, param)
39
- param.post_params = { key => param.post_params } unless param.post_params.empty?
40
- end
41
- private_class_method :fix_put_parameters
42
-
43
- private
44
-
45
- # Adds the authentication email and token to the request.
46
- def add_authentication(_, request)
47
- request.headers['X-User-Email'] = self.class.api_user_email
48
- request.headers['X-User-Token'] = self.class.api_token
49
- end
50
- end
1
+ # frozen_string_literal: true
2
+ class Coursemology::Evaluator::Models::Base < Flexirest::Base
3
+ class << self
4
+ attr_accessor :api_user_email
5
+ attr_accessor :api_token
6
+
7
+ def initialize
8
+ Flexirest::Base.perform_caching = false
9
+ default_config = Flexirest::Base.faraday_config
10
+ Flexirest::Base.faraday_config do |faraday|
11
+ # +follow_redirects+ must be added before declaring the adapter. See faraday_middleware#32,
12
+ # last comment.
13
+ faraday.response :follow_redirects
14
+
15
+ default_config.call(faraday)
16
+ end
17
+ end
18
+ end
19
+
20
+ verbose!
21
+ before_request :add_authentication
22
+
23
+ # Sets the key of the model. This is the key that all attributes are nested under, the same as
24
+ # the +require+ directive in the controller of the web application.
25
+ #
26
+ # @param [String] key The key to prefix all attributes with.
27
+ def self.model_key(key)
28
+ before_request do |name, param|
29
+ fix_put_parameters(key, name, param) if [:post, :patch, :put].include?(param.method[:method])
30
+ end
31
+ end
32
+ private_class_method :model_key
33
+
34
+ # Fixes the request parameters when executing a POST, PATCH or PUT.
35
+ #
36
+ # @param [String] key The key to prefix all attributes with.
37
+ # @param [Request] param The request parameter to prepend the key with.
38
+ def self.fix_put_parameters(key, _, param)
39
+ param.post_params = { key => param.post_params } unless param.post_params.empty?
40
+ end
41
+ private_class_method :fix_put_parameters
42
+
43
+ private
44
+
45
+ # Adds the authentication email and token to the request.
46
+ def add_authentication(_, request)
47
+ request.headers['X-User-Email'] = self.class.api_user_email
48
+ request.headers['X-User-Token'] = self.class.api_token
49
+ end
50
+ end
@@ -1,67 +1,55 @@
1
- # frozen_string_literal: true
2
- class Coursemology::Evaluator::Models::ProgrammingEvaluation < Coursemology::Evaluator::Models::Base
3
- extend ActiveSupport::Autoload
4
- autoload :Package
5
-
6
- request_body_type :json
7
- model_key :programming_evaluation
8
-
9
- get :find, 'courses/assessment/programming_evaluations/:id'.freeze
10
- post :allocate, 'courses/assessment/programming_evaluations/allocate'.freeze
11
- put :save, 'courses/assessment/programming_evaluations/:id/result'.freeze
12
-
13
- # Gets the language for the programming evaluation.
14
- #
15
- # @return [nil] If the language does not exist.
16
- # @return [Coursemology::Polyglot::Language] The language that the evaluation uses.
17
- def language
18
- Coursemology::Polyglot::Language.find_by(type: super)
19
- end
20
-
21
- # Sets the language for the programming evaluation.
22
- #
23
- # @param [String|nil|Coursemology::Polyglot::Language] language The language to set. If this is
24
- # a string, it is assumed to be the class name of the language.
25
- def language=(language)
26
- return super(language) if language.nil? || language.is_a?(String)
27
-
28
- fail ArgumentError unless language.is_a?(Coursemology::Polyglot::Language)
29
- super(language.class.name)
30
- end
31
-
32
- # Obtains the package.
33
- #
34
- # @return [Coursemology::Evaluator::Models::ProgrammingEvaluation::Package]
35
- def package
36
- @package ||= begin
37
- body = plain_request('courses/assessment/programming_evaluations/:id/package', id: id)
38
- Package.new(Coursemology::Evaluator::StringIO.new(body))
39
- end
40
- end
41
-
42
- # Evaluates the package, and stores the result in this record.
43
- #
44
- # Call {Coursemology::Evaluator::Models::ProgrammingEvaluation#save} to save the record to the
45
- # server.
46
- def evaluate
47
- result = Coursemology::Evaluator::Services::EvaluateProgrammingPackageService.
48
- execute(self)
49
- self.stdout = result.stdout
50
- self.stderr = result.stderr
51
- self.test_report = result.test_report
52
- self.exit_code = result.exit_code
53
- end
54
-
55
- private
56
-
57
- # Performs a plain request.
58
- #
59
- # @param [String] url The URL to request.
60
- # @param [Hash] params The parameter to be part of the request.
61
- # @return [String] The response body.
62
- def plain_request(url, params = {})
63
- request = Flexirest::Request.new({ url: url, method: :get, options: { plain: true } },
64
- self.class)
65
- request.call(params)
66
- end
67
- end
1
+ # frozen_string_literal: true
2
+ class Coursemology::Evaluator::Models::ProgrammingEvaluation < Coursemology::Evaluator::Models::Base
3
+ extend ActiveSupport::Autoload
4
+ autoload :Package
5
+
6
+ request_body_type :json
7
+ model_key :programming_evaluation
8
+
9
+ get :find, 'courses/assessment/programming_evaluations/:id'.freeze
10
+ post :allocate, 'courses/assessment/programming_evaluations/allocate'.freeze
11
+ put :save, 'courses/assessment/programming_evaluations/:id/result'.freeze
12
+
13
+ # Gets the language for the programming evaluation.
14
+ #
15
+ # @return [nil] If the language does not exist.
16
+ # @return [Coursemology::Polyglot::Language] The language that the evaluation uses.
17
+ def language
18
+ Coursemology::Polyglot::Language.find_by(type: super)
19
+ end
20
+
21
+ # Sets the language for the programming evaluation.
22
+ #
23
+ # @param [String|nil|Coursemology::Polyglot::Language] language The language to set. If this is
24
+ # a string, it is assumed to be the class name of the language.
25
+ def language=(language)
26
+ return super(language) if language.nil? || language.is_a?(String)
27
+
28
+ fail ArgumentError unless language.is_a?(Coursemology::Polyglot::Language)
29
+ super(language.class.name)
30
+ end
31
+
32
+ # Obtains the package.
33
+ #
34
+ # @return [Coursemology::Evaluator::Models::ProgrammingEvaluation::Package]
35
+ def package
36
+ @package ||= begin
37
+ body = self.class._plain_request('courses/assessment/programming_evaluations/:id/package',
38
+ :get, id: id)
39
+ Package.new(Coursemology::Evaluator::StringIO.new(body))
40
+ end
41
+ end
42
+
43
+ # Evaluates the package, and stores the result in this record.
44
+ #
45
+ # Call {Coursemology::Evaluator::Models::ProgrammingEvaluation#save} to save the record to the
46
+ # server.
47
+ def evaluate
48
+ result = Coursemology::Evaluator::Services::EvaluateProgrammingPackageService.
49
+ execute(self)
50
+ self.stdout = result.stdout
51
+ self.stderr = result.stderr
52
+ self.test_report = result.test_report
53
+ self.exit_code = result.exit_code
54
+ end
55
+ end