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
data/app/models/discussion.rb
CHANGED
@@ -2,7 +2,7 @@ class Discussion < ApplicationRecord
|
|
2
2
|
include WithDiscussionStatus, ParentNavigation, WithScopedQueries, Contextualization
|
3
3
|
|
4
4
|
belongs_to :item, polymorphic: true
|
5
|
-
has_many :messages, -> { order(:created_at) }
|
5
|
+
has_many :messages, -> { order(:created_at) }, dependent: :delete_all
|
6
6
|
belongs_to :initiator, class_name: 'User'
|
7
7
|
belongs_to :exercise, foreign_type: :exercise, foreign_key: 'item_id'
|
8
8
|
has_many :subscriptions
|
@@ -123,6 +123,12 @@ class Discussion < ApplicationRecord
|
|
123
123
|
responses_count > 0
|
124
124
|
end
|
125
125
|
|
126
|
+
def extra_preview_html
|
127
|
+
# FIXME this is buggy, because the extra
|
128
|
+
# may have changed since the submission of this discussion
|
129
|
+
exercise.assignment_for(initiator).extra_preview_html
|
130
|
+
end
|
131
|
+
|
126
132
|
def self.debatable_for(klazz, params)
|
127
133
|
debatable_id = params[:"#{klazz.underscore}_id"]
|
128
134
|
klazz.constantize.find(debatable_id)
|
data/app/models/exam.rb
CHANGED
@@ -126,4 +126,18 @@ class Exam < ApplicationRecord
|
|
126
126
|
exams.destroy_all
|
127
127
|
end
|
128
128
|
end
|
129
|
+
|
130
|
+
def attempts_left_for(assignment)
|
131
|
+
max_attempts_for(assignment) - (assignment.attempts_count || 0)
|
132
|
+
end
|
133
|
+
|
134
|
+
def limited?
|
135
|
+
true
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
def max_attempts_for(assignment)
|
141
|
+
assignment.choice? ? max_choice_submissions : max_problem_submissions
|
142
|
+
end
|
129
143
|
end
|
data/app/models/exercise.rb
CHANGED
@@ -22,6 +22,7 @@ class Exercise < ApplicationRecord
|
|
22
22
|
:guide
|
23
23
|
|
24
24
|
randomize :description, :hint, :extra, :test, :default_content
|
25
|
+
delegate :limited?, :timed?, to: :navigable_parent
|
25
26
|
|
26
27
|
def console?
|
27
28
|
queriable?
|
@@ -69,7 +70,7 @@ class Exercise < ApplicationRecord
|
|
69
70
|
end
|
70
71
|
|
71
72
|
def submission_for(user)
|
72
|
-
assignment_for(user)
|
73
|
+
assignment_for(user).submission
|
73
74
|
end
|
74
75
|
|
75
76
|
def new_solution
|
@@ -142,6 +143,21 @@ class Exercise < ApplicationRecord
|
|
142
143
|
{}
|
143
144
|
end
|
144
145
|
|
146
|
+
def default_content
|
147
|
+
self[:default_content] || ''
|
148
|
+
end
|
149
|
+
|
150
|
+
# Submits the user solution
|
151
|
+
# only if the corresponding assignment has attemps left
|
152
|
+
def try_submit_solution!(user, solution={})
|
153
|
+
assignment = assignment_for(user)
|
154
|
+
if assignment.attempts_left?
|
155
|
+
submit_solution!(user, solution)
|
156
|
+
else
|
157
|
+
assignment
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
145
161
|
private
|
146
162
|
|
147
163
|
def evaluation_class
|
data/app/models/guide.rb
CHANGED
data/app/models/language.rb
CHANGED
@@ -65,6 +65,20 @@ class Language < ApplicationRecord
|
|
65
65
|
new_directive Mumukit::Directives::Sections
|
66
66
|
end
|
67
67
|
|
68
|
+
|
69
|
+
def assets_urls_for(kind, content_type)
|
70
|
+
send "#{kind}_#{content_type}_urls"
|
71
|
+
end
|
72
|
+
|
73
|
+
# TODO this should be a Mumukit::Directives::Directive
|
74
|
+
# and be part of a pipeline
|
75
|
+
def interpolate_references_for(assignment, field)
|
76
|
+
interpolate(field, assignment.submitter.interpolations, lambda { |content| replace_content_reference(assignment, content) })
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
# TODO we should use Mumukit::Directives::Pipeline
|
68
82
|
def interpolate(interpolee, *interpolations)
|
69
83
|
interpolations.inject(interpolee) { |content, interpolation| directives_interpolations.interpolate(content, interpolation).first }
|
70
84
|
end
|
@@ -73,17 +87,20 @@ class Language < ApplicationRecord
|
|
73
87
|
new_directive Mumukit::Directives::Interpolations
|
74
88
|
end
|
75
89
|
|
76
|
-
def
|
77
|
-
|
90
|
+
def replace_content_reference(assignment, interpolee)
|
91
|
+
case interpolee
|
92
|
+
when /previousContent|previousSolution/
|
93
|
+
assignment.current_content_at(-1)
|
94
|
+
when /(solution|content)\[(-?\d*)\]/
|
95
|
+
assignment.current_content_at($2.to_i)
|
96
|
+
end
|
78
97
|
end
|
79
98
|
|
80
|
-
def assets_urls_for(kind, content_type)
|
81
|
-
send "#{kind}_#{content_type}_urls"
|
82
|
-
end
|
83
|
-
|
84
|
-
private
|
85
|
-
|
86
99
|
def new_directive(directive_type)
|
87
100
|
directive_type.new.tap { |it| it.comment_type = directives_comment_type }
|
88
101
|
end
|
102
|
+
|
103
|
+
def directives_comment_type
|
104
|
+
Mumukit::Directives::CommentType.parse comment_type
|
105
|
+
end
|
89
106
|
end
|
@@ -29,9 +29,9 @@
|
|
29
29
|
<%= solution_download_link assignment %>
|
30
30
|
<% end %>
|
31
31
|
|
32
|
-
<% if assignment.
|
33
|
-
<%= assistance_box assignment %>
|
34
|
-
<% else %>
|
32
|
+
<% if assignment.passed? %>
|
35
33
|
<%= corollary_box @exercise %>
|
34
|
+
<% else %>
|
35
|
+
<%= assistance_box assignment %>
|
36
36
|
<% end %>
|
37
37
|
<%= render partial: 'exercise_solutions/results_button', locals: {assignment: assignment} %>
|
@@ -17,5 +17,5 @@
|
|
17
17
|
<div class="description"><%= exercise.description_task %></div>
|
18
18
|
<div class="hint" style="display: none"><%= exercise.hint_html %></div>
|
19
19
|
</div>
|
20
|
-
<div class="mu-kids-character-speech-bubble-failed <%= @assignment
|
20
|
+
<div class="mu-kids-character-speech-bubble-failed <%= @assignment.status %>" style="display: none"></div>
|
21
21
|
</div>
|
@@ -7,7 +7,7 @@
|
|
7
7
|
<%= image_tag(org.banner_url, height: 50, class: 'pull-left') %>
|
8
8
|
</div>
|
9
9
|
<div class="col-md-6 organization-row">
|
10
|
-
<%= link_to(t(:go_to, organization: org.name), org
|
10
|
+
<%= link_to(t(:go_to, organization: org.name), organization_switch_url(org), class: 'btn btn-success pull-right') %>
|
11
11
|
</div>
|
12
12
|
</div>
|
13
13
|
<% end %>
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<%= hidden_field_tag :console_endpoint, exercise_tries_path(exercise) %>
|
2
|
-
<%= hidden_field_tag :historical_queries, @assignment
|
2
|
+
<%= hidden_field_tag :historical_queries, @assignment.queries_with_results.to_json %>
|
3
3
|
|
4
4
|
<% if exercise.extra_visible? %>
|
5
5
|
<ul class="nav nav-tabs" role="tablist">
|
@@ -12,7 +12,7 @@
|
|
12
12
|
|
13
13
|
<div class="tab-content">
|
14
14
|
<div role="tabpanel" class="tab-pane mu-input-panel fade in active <%= pending_messages_filter(@assignment) %>" id="console">
|
15
|
-
<% if @assignment
|
15
|
+
<% if @assignment.pending_messages? %>
|
16
16
|
<span class="pending-messages-text"> <%= t :pending_messages_explanation %>
|
17
17
|
<a href="javascript:{}"
|
18
18
|
onclick="mumuki.Chat.submitMessagesForm('<%= messages_url(exercise) %>', '<%= read_messages_path(exercise) %>')"
|
@@ -29,18 +29,18 @@
|
|
29
29
|
</div>
|
30
30
|
</div>
|
31
31
|
<div role="tabpanel" class="tab-pane mu-input-panel fade" id="visible-extra">
|
32
|
-
<%=
|
32
|
+
<%= @assignment.extra_preview_html %>
|
33
33
|
</div>
|
34
34
|
<div role="tabpanel" class="tab-pane mu-input-panel fade" id="messages">
|
35
|
-
<%= render partial: 'layouts/messages', locals: {messages: @assignment.messages}
|
35
|
+
<%= render partial: 'layouts/messages', locals: {messages: @assignment.messages} unless @assignment.pending? %>
|
36
36
|
</div>
|
37
37
|
</div>
|
38
|
-
|
38
|
+
<%# FIXME Duplicated view in problem_form %>
|
39
39
|
<% content_for :submission_results do %>
|
40
40
|
<% if current_user? %>
|
41
41
|
<div class="row">
|
42
42
|
<div class="col-md-12 submission-results">
|
43
|
-
<% if @assignment
|
43
|
+
<% if @assignment.passed? %>
|
44
44
|
<%= render partial: 'exercise_solutions/results',
|
45
45
|
locals: {assignment: @assignment, guide_finished_by_solution: false} %>
|
46
46
|
<% end %>
|
@@ -4,7 +4,7 @@
|
|
4
4
|
<%= render_exercise_input_editor f, exercise %>
|
5
5
|
|
6
6
|
<div class="actions mu-kids-submit-button">
|
7
|
-
<%= render_submit_button(
|
7
|
+
<%= render_submit_button(@assignment) %>
|
8
8
|
</div>
|
9
9
|
<div class="actions mu-kids-reset-button"></div>
|
10
10
|
<img class="mu-kids-compass-rose" src="/compass_rose.svg">
|
@@ -18,7 +18,7 @@
|
|
18
18
|
|
19
19
|
<div class="tab-content">
|
20
20
|
<div role="tabpanel" class="tab-pane mu-input-panel fade in active" id="editor">
|
21
|
-
<% if @assignment
|
21
|
+
<% if @assignment.pending_messages? %>
|
22
22
|
<span class="pending-messages-text"> <%= t :pending_messages_explanation %>
|
23
23
|
<a href="javascript:{}"
|
24
24
|
onclick="mumuki.Chat.submitMessagesForm('<%= messages_url(exercise) %>', '<%= read_messages_path(exercise) %>')"
|
@@ -34,7 +34,7 @@
|
|
34
34
|
<%= render_exercise_input_editor f, exercise %>
|
35
35
|
|
36
36
|
<div class="actions">
|
37
|
-
<%= render_submit_button(
|
37
|
+
<%= render_submit_button(@assignment) %>
|
38
38
|
</div>
|
39
39
|
<% end %>
|
40
40
|
</div>
|
@@ -52,10 +52,10 @@
|
|
52
52
|
</div>
|
53
53
|
</div>
|
54
54
|
<div role="tabpanel" class="tab-pane mu-input-panel fade" id="visible-extra">
|
55
|
-
<%=
|
55
|
+
<%= @assignment.extra_preview_html %>
|
56
56
|
</div>
|
57
57
|
<div role="tabpanel" class="tab-pane mu-input-panel fade" id="messages">
|
58
|
-
<%= render partial: 'layouts/messages', locals: {messages: @assignment.messages}
|
58
|
+
<%= render partial: 'layouts/messages', locals: {messages: @assignment.messages} unless @assignment.pending? %>
|
59
59
|
</div>
|
60
60
|
</div>
|
61
61
|
|
@@ -63,7 +63,7 @@
|
|
63
63
|
<% if current_user? %>
|
64
64
|
<div class="row">
|
65
65
|
<div class="col-md-12 submission-results">
|
66
|
-
<%
|
66
|
+
<% unless @assignment.pending? %>
|
67
67
|
<%= render partial: 'exercise_solutions/results',
|
68
68
|
locals: {assignment: @assignment} %>
|
69
69
|
<% end %>
|
@@ -9,7 +9,7 @@
|
|
9
9
|
<div class="col-md-12">
|
10
10
|
<div class="row">
|
11
11
|
<div class="col-md-12 submission-results">
|
12
|
-
<% if @assignment
|
12
|
+
<% if @assignment.passed? %>
|
13
13
|
<%= render partial: 'exercise_solutions/kids_results',
|
14
14
|
locals: {assignment: @assignment, guide_finished_by_solution: false} %>
|
15
15
|
<% end %>
|
@@ -2,7 +2,7 @@ module Mumuki::Laboratory::Controllers::ResultsRendering
|
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
|
4
4
|
included do
|
5
|
-
include ProgressBarHelper
|
5
|
+
include ProgressBarHelper, ExerciseInputHelper
|
6
6
|
|
7
7
|
before_action :set_guide_previously_done!
|
8
8
|
end
|
@@ -14,16 +14,18 @@ module Mumuki::Laboratory::Controllers::ResultsRendering
|
|
14
14
|
html: render_results_html(assignment),
|
15
15
|
title_html: render_results_title_html(assignment),
|
16
16
|
button_html: render_results_button_html(assignment),
|
17
|
-
expectations_html: render_results_expectations_html(assignment)
|
17
|
+
expectations_html: render_results_expectations_html(assignment),
|
18
|
+
remaining_attempts_html: remaining_attempts_text(assignment))
|
18
19
|
end
|
19
20
|
|
20
21
|
def render_results_html(assignment)
|
21
|
-
render_to_string partial: results_partial,
|
22
|
+
render_to_string partial: results_partial(assignment),
|
22
23
|
locals: {assignment: assignment}
|
23
24
|
end
|
24
25
|
|
25
|
-
def results_partial
|
26
|
-
|
26
|
+
def results_partial(assignment)
|
27
|
+
return 'out_of_attempts' unless assignment.attempts_left?
|
28
|
+
assignment.input_kids? ? 'exercise_solutions/kids_results' : 'exercise_solutions/results'
|
27
29
|
end
|
28
30
|
|
29
31
|
def render_results_title_html(assignment)
|
@@ -15,6 +15,9 @@ en:
|
|
15
15
|
ask_community: Ask community for help
|
16
16
|
ask_redirect: Do you want to go there?
|
17
17
|
ask_the_first_question: Be the first one to ask!
|
18
|
+
attempts_left:
|
19
|
+
zero: "You've run out of attempts "
|
20
|
+
other: '%{count} attempts remaining'
|
18
21
|
author: Author
|
19
22
|
authoring: "Authoring"
|
20
23
|
authoring_note: This guide's content was developed by %{authors} and <a href="%{collaborators}" target="_blank">many others</a>, under the terms of <a href="https://creativecommons.org/licenses/by-sa/4.0/" target="_blank">Creative Commons License Share-Alike, 4.0</a>
|
@@ -143,6 +146,7 @@ en:
|
|
143
146
|
office: Office
|
144
147
|
opened: Open
|
145
148
|
opened_count: '%{count} opened'
|
149
|
+
out_of_attempts: You've run out of attempts. You should proceed to the next exercise!
|
146
150
|
output: Output
|
147
151
|
overview: Overview
|
148
152
|
passed: Everything is in order! Your solution passed all our tests!
|
@@ -16,6 +16,10 @@ es:
|
|
16
16
|
ask_community: Preguntá a la comunidad
|
17
17
|
ask_redirect: ¿Querés que te llevemos ahí?
|
18
18
|
ask_the_first_question: ¡Hacé la primera pregunta!
|
19
|
+
attempts_left:
|
20
|
+
zero: 'Te quedaste sin intentos'
|
21
|
+
one: '1 intento restante'
|
22
|
+
other: '%{count} intentos restantes'
|
19
23
|
author: Autor
|
20
24
|
authoring: "Autores"
|
21
25
|
authoring_note: Esta guía fue desarrollada por %{authors} y <a href="%{collaborators}" target="_blank">muchas personas más</a>, bajo los términos de la <a href="https://creativecommons.org/licenses/by-sa/4.0/" target="_blank">Licencia Creative Commons Compartir-Igual, 4.0</a>.
|
@@ -161,6 +165,7 @@ es:
|
|
161
165
|
one: '1 abierta'
|
162
166
|
other: '%{count} abiertas'
|
163
167
|
organizations: Organizaciones
|
168
|
+
out_of_attempts: Te quedaste sin intentos. ¡Seguí al proximo ejercicio!
|
164
169
|
output: Salida
|
165
170
|
overview: Vista general
|
166
171
|
passed: ¡Muy bien! Tu solución pasó todas las pruebas
|
@@ -16,6 +16,9 @@ pt:
|
|
16
16
|
ask_community: Pergunte à comunidade
|
17
17
|
ask_redirect: Você quer que nós o levemos para lá?
|
18
18
|
ask_the_first_question: Faça a primeira pergunta!
|
19
|
+
attempts_left:
|
20
|
+
zero: 'Você ficou sem tentativas'
|
21
|
+
other: '%{count} tentativa restante'
|
19
22
|
author: Autor
|
20
23
|
authoring: Autores
|
21
24
|
authoring_note: 'Este guia foi desenvolvido por %{authors} e <a href="%{collaborators}" target="_blank"> muitas outras pessoas </a>, nos termos do <a href = "https://creativecommons.org/licenses/by-sa/4.0/" target="_blank"> Creative Commons License Share-Equal, 4.0 </a>.'
|
@@ -154,6 +157,7 @@ pt:
|
|
154
157
|
opened: Aberto
|
155
158
|
opened_count: '%{count} aberto'
|
156
159
|
organizations: Organizações
|
160
|
+
out_of_attempts: Você ficou sem tentativas. Você deve prosseguir para o próximo exercício!
|
157
161
|
output: Sair
|
158
162
|
overview: Visão global
|
159
163
|
passed: Muito bem! Sua solução passou todos os testes
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'mumukit/directives'
|
2
2
|
|
3
3
|
class Mumukit::Directives::Sections < Mumukit::Directives::Directive
|
4
|
+
# TODO Move this behaviour to gem
|
4
5
|
def join(sections)
|
5
6
|
file_declarations, file_references = sections.map do |section, content|
|
6
7
|
[build(section, content), interpolate(section)]
|