mumuki-laboratory 5.8.3 → 5.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/application/submission.js +25 -4
- data/app/controllers/application_controller.rb +4 -1
- data/app/controllers/concerns/organizations_controller_template.rb +3 -2
- data/app/controllers/exercise_solutions_controller.rb +1 -1
- data/app/controllers/exercises_controller.rb +3 -7
- data/app/controllers/login_controller.rb +6 -0
- data/app/helpers/contextualization_result_helper.rb +1 -1
- data/app/helpers/exercise_input_helper.rb +20 -5
- data/app/helpers/messages_helper.rb +4 -4
- data/app/helpers/organization_list_helper.rb +4 -0
- data/app/models/assignment.rb +50 -7
- data/app/models/concerns/contextualization.rb +3 -3
- data/app/models/concerns/guide_container.rb +15 -0
- data/app/models/concerns/submittable/submittable.rb +1 -1
- data/app/models/concerns/with_assignments.rb +10 -41
- data/app/models/concerns/with_discussions.rb +1 -1
- data/app/models/discussion.rb +7 -1
- data/app/models/exam.rb +14 -0
- data/app/models/exercise.rb +17 -1
- data/app/models/exercise/challenge.rb +1 -5
- data/app/models/guide.rb +1 -1
- data/app/models/language.rb +25 -8
- data/app/models/submission/submission.rb +1 -1
- data/app/views/discussions/_message.html.erb +1 -1
- data/app/views/exercise_solutions/_out_of_attempts.html.erb +6 -0
- data/app/views/exercise_solutions/_results.html.erb +3 -3
- data/app/views/exercises/_read_only.html.erb +1 -1
- data/app/views/layouts/_kids.html.erb +1 -1
- data/app/views/layouts/_organizations_listing.html.erb +1 -1
- data/app/views/layouts/exercise_inputs/forms/_interactive_form.html.erb +6 -6
- data/app/views/layouts/exercise_inputs/forms/_kids_form.html.erb +1 -1
- data/app/views/layouts/exercise_inputs/forms/_playground_form.html.erb +1 -1
- data/app/views/layouts/exercise_inputs/forms/_problem_form.html.erb +5 -5
- data/app/views/layouts/exercise_inputs/read_only_editors/_text.erb +3 -0
- data/app/views/layouts/modals/_kids_results.html.erb +1 -1
- data/db/migrate/20180725145801_add_submissions_caps_to_exams.rb +6 -0
- data/lib/mumuki/laboratory/controllers/results_rendering.rb +7 -5
- data/lib/mumuki/laboratory/locales/en.yml +4 -0
- data/lib/mumuki/laboratory/locales/es.yml +5 -0
- data/lib/mumuki/laboratory/locales/pt.yml +4 -0
- data/lib/mumuki/laboratory/mumukit/directives.rb +1 -0
- data/lib/mumuki/laboratory/status.rb +4 -0
- data/lib/mumuki/laboratory/status/discussion/discussion.rb +2 -14
- data/lib/mumuki/laboratory/status/submission/aborted.rb +4 -4
- data/lib/mumuki/laboratory/status/submission/errored.rb +4 -0
- data/lib/mumuki/laboratory/status/submission/failed.rb +4 -0
- data/lib/mumuki/laboratory/status/submission/manual_evaluation_pending.rb +1 -1
- data/lib/mumuki/laboratory/status/submission/passed.rb +0 -4
- data/lib/mumuki/laboratory/status/submission/passed_with_warnings.rb +5 -1
- data/lib/mumuki/laboratory/status/submission/pending.rb +4 -0
- data/lib/mumuki/laboratory/status/submission/running.rb +4 -0
- data/lib/mumuki/laboratory/status/submission/submission.rb +7 -17
- data/lib/mumuki/laboratory/version.rb +1 -1
- data/spec/controllers/discussions_controller_spec.rb +7 -0
- data/spec/controllers/exercise_solutions_controller_spec.rb +6 -1
- data/spec/controllers/messages_controller_spec.rb +7 -0
- data/spec/dummy/db/schema.rb +2 -0
- data/spec/factories/exam_factory.rb +1 -2
- data/spec/features/progressive_tips_spec.rb +4 -4
- data/spec/helpers/organization_list_helper_spec.rb +20 -0
- data/spec/models/assignment_spec.rb +2 -0
- data/spec/models/event_generation_spec.rb +6 -6
- data/spec/models/exercise_spec.rb +39 -32
- data/spec/models/guide_spec.rb +2 -2
- data/spec/models/interactive_spec.rb +1 -1
- data/spec/models/query_spec.rb +1 -2
- data/spec/models/question_spec.rb +1 -3
- data/spec/models/solution_spec.rb +33 -2
- data/spec/spec_helper.rb +1 -0
- data/vendor/assets/javascripts/codemirror-modes/php.js +236 -0
- metadata +14 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1aba4e2ab138f4c9e7310afc18ca35639c12d06c70980fd9d912abf24af1c937
|
4
|
+
data.tar.gz: 98afb11476a5221d39783b233ec4f7e1e05ec569c62a1c14a78a4bf138b91ab7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8b6e21ceb45a76cfd5eb468271b30dbfe095efb33bf3a5f01b715721a9bba2a20e8dfb76a859379bf7a495e9abe1f7cdf5d6b048f3841d2dca645360a9ee8a2b
|
7
|
+
data.tar.gz: 1b8f6d4d19ba9e689d62f7e96e2b9d5cb985517d9c29a0b4b49f594fa3c7d5cfdbbefef408994e4af6eed85578f1b8ba1659db92de5d007f7773c625b6daeef8
|
@@ -22,7 +22,8 @@ var mumuki = mumuki || {};
|
|
22
22
|
this.submissionsErrorTemplate.show();
|
23
23
|
animateTimeoutError(submitButton);
|
24
24
|
},
|
25
|
-
done: function () {
|
25
|
+
done: function (data, submitButton) {
|
26
|
+
submitButton.updateAttemptsLeft(data);
|
26
27
|
mumuki.pin.scroll();
|
27
28
|
}
|
28
29
|
};
|
@@ -34,7 +35,6 @@ var mumuki = mumuki || {};
|
|
34
35
|
|
35
36
|
SubmitButton.prototype = {
|
36
37
|
disable: function () {
|
37
|
-
this.setWaitingText();
|
38
38
|
this.submissionControls.attr('disabled', 'disabled');
|
39
39
|
},
|
40
40
|
setWaitingText: function () {
|
@@ -47,6 +47,24 @@ var mumuki = mumuki || {};
|
|
47
47
|
enable: function () {
|
48
48
|
this.setSendText();
|
49
49
|
this.submissionControls.removeAttr('disabled');
|
50
|
+
},
|
51
|
+
reachedMaxAttempts: function () {
|
52
|
+
return $('#attempts-left-text').attr('data-disabled') === "true";
|
53
|
+
},
|
54
|
+
updateAttemptsLeft: function (data) {
|
55
|
+
$('#attempts-left-text').replaceWith(data['remaining_attempts_html']);
|
56
|
+
this.checkAttemptsLeft();
|
57
|
+
},
|
58
|
+
preventSubmission: function () {
|
59
|
+
this.disable();
|
60
|
+
this.submitButton.on('click', function (e) {
|
61
|
+
e.preventDefault();
|
62
|
+
})
|
63
|
+
},
|
64
|
+
checkAttemptsLeft: function () {
|
65
|
+
if (this.reachedMaxAttempts()) {
|
66
|
+
this.preventSubmission();
|
67
|
+
}
|
50
68
|
}
|
51
69
|
};
|
52
70
|
|
@@ -58,6 +76,7 @@ var mumuki = mumuki || {};
|
|
58
76
|
|
59
77
|
var btnSubmit = $('.btn-submit');
|
60
78
|
var submissionControl = $('.submission_control');
|
79
|
+
|
61
80
|
var submitButton = new SubmitButton(btnSubmit, submissionControl);
|
62
81
|
|
63
82
|
var bridge = new mumuki.bridge.Laboratory;
|
@@ -65,6 +84,7 @@ var mumuki = mumuki || {};
|
|
65
84
|
btnSubmit.on('click', function (e) {
|
66
85
|
e.preventDefault();
|
67
86
|
submitButton.disable();
|
87
|
+
submitButton.setWaitingText();
|
68
88
|
resultsBox.waiting();
|
69
89
|
|
70
90
|
mumuki.editor.syncContent();
|
@@ -74,12 +94,13 @@ var mumuki = mumuki || {};
|
|
74
94
|
resultsBox.success(data, submitButton);
|
75
95
|
}).fail(function () {
|
76
96
|
resultsBox.error(submitButton);
|
77
|
-
}).always(function () {
|
97
|
+
}).always(function (data) {
|
78
98
|
$(document).renderMuComponents();
|
79
|
-
resultsBox.done();
|
99
|
+
resultsBox.done(data, submitButton);
|
80
100
|
});
|
81
101
|
});
|
82
102
|
|
103
|
+
submitButton.checkAttemptsLeft();
|
83
104
|
});
|
84
105
|
|
85
106
|
function getContent(){
|
@@ -1,6 +1,8 @@
|
|
1
1
|
class ApplicationController < ActionController::Base
|
2
2
|
Mumukit::Login.configure_controller! self
|
3
3
|
|
4
|
+
protect_from_forgery with: :exception
|
5
|
+
|
4
6
|
include Mumuki::Laboratory::Controllers::CurrentOrganization
|
5
7
|
include Mumukit::Login::AuthenticationHelpers
|
6
8
|
|
@@ -41,7 +43,8 @@ class ApplicationController < ActionController::Base
|
|
41
43
|
|
42
44
|
# ensures contents are accessible to current user
|
43
45
|
def validate_accessible!
|
44
|
-
|
46
|
+
return if current_user&.teacher_here?
|
47
|
+
accessible_subject.validate_accessible_for! current_user
|
45
48
|
end
|
46
49
|
|
47
50
|
# required by Mumukit::Login
|
@@ -28,8 +28,9 @@ module OrganizationsControllerTemplate
|
|
28
28
|
:logo_url, :banner_url, :open_graph_image_url, :favicon_url, :breadcrumb_image_url,
|
29
29
|
:raise_hand_enabled, :report_issue_enabled, :forum_enabled,
|
30
30
|
:feedback_suggestions_enabled, :public, :immersive,
|
31
|
-
:theme_stylesheet, :extension_javascript,
|
32
|
-
:
|
31
|
+
:theme_stylesheet, :extension_javascript, :terms_of_service,
|
32
|
+
:community_link, :login_provider, :embeddable,
|
33
|
+
login_methods: [])
|
33
34
|
.tap { |it| it.merge!(book: Book.find_by!(slug: it[:book])) if it[:book] }
|
34
35
|
end
|
35
36
|
end
|
@@ -7,7 +7,7 @@ class ExerciseSolutionsController < AjaxController
|
|
7
7
|
before_action :validate_accessible!, only: :create
|
8
8
|
|
9
9
|
def create
|
10
|
-
assignment = @exercise.
|
10
|
+
assignment = @exercise.try_submit_solution!(current_user, solution_params)
|
11
11
|
render_results_json assignment, status: assignment.status
|
12
12
|
end
|
13
13
|
|
@@ -3,7 +3,6 @@ class ExercisesController < ApplicationController
|
|
3
3
|
include Mumuki::Laboratory::Controllers::ExerciseSeed
|
4
4
|
|
5
5
|
before_action :set_guide!, only: :show
|
6
|
-
before_action :set_default_content!, only: :show, if: :current_user?
|
7
6
|
before_action :set_assignment!, only: :show, if: :current_user?
|
8
7
|
before_action :validate_accessible!, only: :show
|
9
8
|
before_action :start!, only: :show
|
@@ -31,14 +30,11 @@ class ExercisesController < ApplicationController
|
|
31
30
|
@exercise.navigable_parent.start! current_user
|
32
31
|
end
|
33
32
|
|
34
|
-
def set_default_content!
|
35
|
-
@files = @exercise.files_for(current_user)
|
36
|
-
@current_content = @exercise.current_content_for(current_user)
|
37
|
-
@default_content = @exercise.default_content_for(current_user)
|
38
|
-
end
|
39
|
-
|
40
33
|
def set_assignment!
|
41
34
|
@assignment = @exercise.assignment_for(current_user)
|
35
|
+
@files = @assignment.files
|
36
|
+
@current_content = @assignment.current_content
|
37
|
+
@default_content = @assignment.default_content
|
42
38
|
end
|
43
39
|
|
44
40
|
def set_guide!
|
@@ -1,8 +1,14 @@
|
|
1
1
|
class LoginController < ApplicationController
|
2
2
|
Mumukit::Login.configure_login_controller! self
|
3
3
|
|
4
|
+
skip_before_action :verify_authenticity_token, if: lambda { Rails.env.development? }
|
5
|
+
|
4
6
|
private
|
5
7
|
|
8
|
+
def organization_name
|
9
|
+
params[:organization] || super
|
10
|
+
end
|
11
|
+
|
6
12
|
def login_failure!
|
7
13
|
redirect_to root_path, alert: request.params['message']
|
8
14
|
end
|
@@ -23,7 +23,7 @@ module ContextualizationResultHelper
|
|
23
23
|
|
24
24
|
def render_test_results(contextualization)
|
25
25
|
if contextualization.test_results.present?
|
26
|
-
render partial: 'layouts/test_results', locals: { contextualization: contextualization}
|
26
|
+
render partial: 'layouts/test_results', locals: { contextualization: contextualization }
|
27
27
|
else
|
28
28
|
render partial: 'layouts/result', locals: { contextualization: contextualization }
|
29
29
|
end
|
@@ -47,15 +47,30 @@ module ExerciseInputHelper
|
|
47
47
|
!assignment.passed? && organization.ask_for_help_enabled?
|
48
48
|
end
|
49
49
|
|
50
|
-
def render_submit_button(
|
51
|
-
options = submit_button_options(exercise)
|
50
|
+
def render_submit_button(assignment)
|
51
|
+
options = submit_button_options(assignment.exercise)
|
52
52
|
text = t(options.t) if options.t.present?
|
53
53
|
waiting_text = t(options.waiting_t) if options.waiting_t.present?
|
54
|
-
%Q{
|
54
|
+
%Q{
|
55
|
+
<div class="btn-submit-container">
|
56
|
+
<#{options.tag} for="#{options.for}"
|
55
57
|
class="btn btn-success btn-block btn-submit #{options.classes}"
|
56
58
|
data-waiting="#{waiting_text}">
|
57
|
-
#{fa_icon options.fa_icon
|
58
|
-
|
59
|
+
#{fa_icon options.fa_icon}
|
60
|
+
#{text} #{remaining_attempts_text(assignment)}
|
61
|
+
</#{options.tag}>
|
62
|
+
</div>
|
63
|
+
}.html_safe
|
64
|
+
end
|
65
|
+
|
66
|
+
def remaining_attempts_text(assignment)
|
67
|
+
if assignment.limited?
|
68
|
+
%Q{
|
69
|
+
<span id="attempts-left-text" data-disabled="#{!assignment.attempts_left?}">
|
70
|
+
(#{t(:attempts_left, count: assignment.attempts_left)})
|
71
|
+
</span>
|
72
|
+
}
|
73
|
+
end
|
59
74
|
end
|
60
75
|
|
61
76
|
def render_custom_editor(exercise, read_only=false)
|
@@ -4,19 +4,19 @@ module MessagesHelper
|
|
4
4
|
end
|
5
5
|
|
6
6
|
def hidden_pending(assignment)
|
7
|
-
assignment
|
7
|
+
assignment.pending_messages? ? '' : 'hidden'
|
8
8
|
end
|
9
9
|
|
10
10
|
def disabled_submit_class(assignment)
|
11
|
-
assignment
|
11
|
+
assignment.pending_messages? ? 'disabled' : ''
|
12
12
|
end
|
13
13
|
|
14
14
|
def pending_messages_filter(assignment)
|
15
|
-
'pending-messages-filter' if assignment
|
15
|
+
'pending-messages-filter' if assignment.pending_messages?
|
16
16
|
end
|
17
17
|
|
18
18
|
def read_messages_caption(assignment)
|
19
|
-
assignment
|
19
|
+
assignment.pending_messages? ? :read_messages : :exit
|
20
20
|
end
|
21
21
|
|
22
22
|
def sender_class(message)
|
@@ -2,4 +2,8 @@ module OrganizationListHelper
|
|
2
2
|
def organizations_for(user)
|
3
3
|
(user.accessible_organizations + [Organization.central]).uniq.compact
|
4
4
|
end
|
5
|
+
|
6
|
+
def organization_switch_url(organization)
|
7
|
+
organization.url_for(controller_name == 'users' ? root_path : request.path)
|
8
|
+
end
|
5
9
|
end
|
data/app/models/assignment.rb
CHANGED
@@ -2,17 +2,21 @@ class Assignment < ApplicationRecord
|
|
2
2
|
include Contextualization
|
3
3
|
include WithMessages
|
4
4
|
|
5
|
+
markdown_on :extra_preview
|
6
|
+
|
5
7
|
belongs_to :exercise
|
6
8
|
has_one :guide, through: :exercise
|
7
|
-
has_many :messages, -> { where.not(submission_id: nil).order(date: :desc) }, foreign_key: :submission_id, primary_key: :submission_id
|
9
|
+
has_many :messages, -> { where.not(submission_id: nil).order(date: :desc) }, foreign_key: :submission_id, primary_key: :submission_id, dependent: :delete_all
|
8
10
|
|
9
11
|
belongs_to :submitter, class_name: 'User'
|
10
12
|
|
11
13
|
validates_presence_of :exercise, :submitter
|
12
14
|
|
13
|
-
delegate :language, :name,
|
15
|
+
delegate :language, :name, :navigable_parent,
|
16
|
+
:limited?, :input_kids?, :choice?, to: :exercise
|
14
17
|
|
15
18
|
alias_attribute :status, :submission_status
|
19
|
+
alias_attribute :attempts_count, :attemps_count
|
16
20
|
|
17
21
|
scope :by_exercise_ids, -> (exercise_ids) {
|
18
22
|
where(exercise_id: exercise_ids) if exercise_ids
|
@@ -22,6 +26,11 @@ class Assignment < ApplicationRecord
|
|
22
26
|
joins(:submitter).where('users.name' => usernames) if usernames
|
23
27
|
}
|
24
28
|
|
29
|
+
defaults do
|
30
|
+
self.query_results = []
|
31
|
+
self.expectation_results = []
|
32
|
+
end
|
33
|
+
|
25
34
|
def evaluate_manually!(teacher_evaluation)
|
26
35
|
update! status: teacher_evaluation[:status], manual_evaluation_comment: teacher_evaluation[:manual_evaluation]
|
27
36
|
end
|
@@ -61,11 +70,15 @@ class Assignment < ApplicationRecord
|
|
61
70
|
end
|
62
71
|
|
63
72
|
def test
|
64
|
-
exercise.
|
73
|
+
exercise.test && language.interpolate_references_for(self, exercise.test)
|
65
74
|
end
|
66
75
|
|
67
76
|
def extra
|
68
|
-
exercise.
|
77
|
+
exercise.extra && language.interpolate_references_for(self, exercise.extra)
|
78
|
+
end
|
79
|
+
|
80
|
+
def extra_preview
|
81
|
+
Mumukit::ContentType::Markdown.highlighted_code(language.name, extra)
|
69
82
|
end
|
70
83
|
|
71
84
|
def run_update!
|
@@ -104,7 +117,6 @@ class Assignment < ApplicationRecord
|
|
104
117
|
end
|
105
118
|
|
106
119
|
def as_platform_json
|
107
|
-
navigable_parent = exercise.navigable_parent
|
108
120
|
as_json(except: [:exercise_id, :submission_id, :id, :submitter_id, :solution, :created_at, :updated_at, :submission_status],
|
109
121
|
include: {
|
110
122
|
guide: {
|
@@ -136,8 +148,39 @@ class Assignment < ApplicationRecord
|
|
136
148
|
@tips ||= exercise.assist_with(self)
|
137
149
|
end
|
138
150
|
|
139
|
-
def
|
140
|
-
self.
|
151
|
+
def increment_attempts!
|
152
|
+
self.attempts_count += 1 if should_retry?
|
153
|
+
end
|
154
|
+
|
155
|
+
def attempts_left
|
156
|
+
navigable_parent.attempts_left_for(self)
|
157
|
+
end
|
158
|
+
|
159
|
+
# Tells wether the submitter of this
|
160
|
+
# assignment can keep on sending submissions
|
161
|
+
# which is true for non limited or for assignments
|
162
|
+
# that have not reached their submissions limit
|
163
|
+
def attempts_left?
|
164
|
+
!limited? || attempts_left > 0
|
165
|
+
end
|
166
|
+
|
167
|
+
def current_content
|
168
|
+
solution || default_content
|
169
|
+
end
|
170
|
+
|
171
|
+
def current_content_at(index)
|
172
|
+
exercise.sibling_at(index).assignment_for(submitter).current_content
|
173
|
+
end
|
174
|
+
|
175
|
+
def default_content
|
176
|
+
@default_content ||= language.interpolate_references_for(self, exercise.default_content)
|
177
|
+
end
|
178
|
+
|
179
|
+
def files
|
180
|
+
language
|
181
|
+
.directives_sections
|
182
|
+
.split_sections(current_content)
|
183
|
+
.map { |name, content| Mumuki::Laboratory::File.new name, content }
|
141
184
|
end
|
142
185
|
|
143
186
|
private
|
@@ -28,7 +28,7 @@ module Contextualization
|
|
28
28
|
|
29
29
|
delegate :visible_success_output?, to: :exercise
|
30
30
|
delegate :output_content_type, to: :language
|
31
|
-
delegate :should_retry?, :to_submission_status,
|
31
|
+
delegate :should_retry?, :to_submission_status, *Mumuki::Laboratory::Status::Submission.test_selectors, to: :submission_status
|
32
32
|
delegate :inspection_keywords, to: :exercise
|
33
33
|
end
|
34
34
|
|
@@ -47,11 +47,11 @@ module Contextualization
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def results_visible?
|
50
|
-
(visible_success_output? ||
|
50
|
+
(visible_success_output? || !passed?) && !exercise.choices? && !manual_evaluation_pending?
|
51
51
|
end
|
52
52
|
|
53
53
|
def result_preview
|
54
|
-
result.truncate(100)
|
54
|
+
result.truncate(100) unless passed?
|
55
55
|
end
|
56
56
|
|
57
57
|
def result_html
|
@@ -25,6 +25,21 @@ module GuideContainer
|
|
25
25
|
def validate_accessible_for!(user)
|
26
26
|
end
|
27
27
|
|
28
|
+
# Tells the number of remaning
|
29
|
+
# attemps for a given assignment that belongs to this
|
30
|
+
# container, or `nil`, if this container does not impose
|
31
|
+
# any limit to the number of submissions
|
32
|
+
def attempts_left_for(assignment)
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
# Tells if this guide container
|
37
|
+
# imposes any kind of limit to the number of submission
|
38
|
+
# to its assignments
|
39
|
+
def limited?
|
40
|
+
false
|
41
|
+
end
|
42
|
+
|
28
43
|
def timed?
|
29
44
|
false
|
30
45
|
end
|
@@ -1,63 +1,32 @@
|
|
1
1
|
module WithAssignments
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
|
4
|
+
# TODO we must avoid _for(user) methods when they
|
5
|
+
# are hidden the assignment object, since an assignment already encapsulates
|
6
|
+
# the exercise-user pair, and many times they impose a performance hit,
|
7
|
+
# since in the normal scenario the assignment object already exists
|
8
|
+
|
4
9
|
included do
|
5
10
|
has_many :assignments, dependent: :destroy
|
6
11
|
end
|
7
12
|
|
8
|
-
def current_content_for(user)
|
9
|
-
assignment_for(user)&.solution || default_content_for(user)
|
10
|
-
end
|
11
|
-
|
12
|
-
def files_for(user)
|
13
|
-
language
|
14
|
-
.directives_sections
|
15
|
-
.split_sections(current_content_for user)
|
16
|
-
.map { |name, content| Mumuki::Laboratory::File.new name, content }
|
17
|
-
end
|
18
|
-
|
19
|
-
def interpolate_for(user, field)
|
20
|
-
language.interpolate(field, user.interpolations, lambda { |content| replace_content_reference(user, content) })
|
21
|
-
end
|
22
|
-
|
23
|
-
def default_content_for(user)
|
24
|
-
interpolate_for user, default_content || ''
|
25
|
-
end
|
26
|
-
|
27
|
-
def extra_for(user)
|
28
|
-
interpolate_for user, extra
|
29
|
-
end
|
30
|
-
|
31
|
-
def test_for(user)
|
32
|
-
test && interpolate_for(user, test)
|
33
|
-
end
|
34
|
-
|
35
|
-
def replace_content_reference(user, interpolee)
|
36
|
-
case interpolee
|
37
|
-
when /previousContent|previousSolution/
|
38
|
-
previous.current_content_for(user)
|
39
|
-
when /(solution|content)\[(-?\d*)\]/
|
40
|
-
sibling_at($2.to_i).current_content_for(user)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
13
|
def messages_for(user)
|
45
|
-
assignment_for(user)
|
14
|
+
assignment_for(user).messages
|
46
15
|
end
|
47
16
|
|
48
17
|
def has_messages_for?(user)
|
49
18
|
messages_for(user).present?
|
50
19
|
end
|
51
20
|
|
52
|
-
def
|
21
|
+
def find_assignment_for(user)
|
53
22
|
assignments.find_by(submitter: user)
|
54
23
|
end
|
55
24
|
|
56
25
|
def status_for(user)
|
57
|
-
assignment_for(user).
|
26
|
+
assignment_for(user).status if user
|
58
27
|
end
|
59
28
|
|
60
|
-
def
|
61
|
-
|
29
|
+
def assignment_for(user)
|
30
|
+
find_assignment_for(user) || user.assignments.build(exercise: self)
|
62
31
|
end
|
63
32
|
end
|