mumuki-laboratory 5.9.1 → 5.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/assets/javascripts/application/kids.js +6 -1
- data/app/controllers/concerns/organizations_controller_template.rb +4 -6
- data/app/controllers/discussions_controller.rb +1 -1
- data/app/controllers/guide_progress_controller.rb +12 -1
- data/app/models/book.rb +1 -1
- data/app/models/concerns/guide_container.rb +6 -2
- data/app/models/exam.rb +9 -5
- data/app/models/exercise.rb +5 -1
- data/app/models/guide.rb +3 -0
- data/app/views/layouts/_guide.html.erb +1 -1
- data/lib/mumuki/laboratory/version.rb +1 -1
- data/lib/mumuki/laboratory.rb +1 -1
- data/spec/controllers/guide_progress_controller_spec.rb +21 -0
- data/spec/features/exams_flow_spec.rb +15 -4
- data/spec/features/guide_reset_spec.rb +17 -1
- data/spec/models/book_import_spec.rb +20 -10
- metadata +11 -10
- data/lib/mumuki/laboratory/mumukit/directives.rb +0 -20
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d5fa7aa2c319aee6bd932340664e1ed91d87d3dcee8f9b8f429301a7e02b2526
|
|
4
|
+
data.tar.gz: d12b201ddb14ba04dfbe233d5e57fcec1b57260c85a622243ed03de1226550d2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d5cc2dcc2980e75f1446a5f9b8c315efd3030e0ce90084edf406cb1303442d91870515d82d13051229458fe34c2bd60ce2e5a7560462188d806bd7b39cbc0bde
|
|
7
|
+
data.tar.gz: 013b96fa9769ba74eba05dd26822872f35acc05e9e60f93b88cdf0c5d81bab5822dc025bb9f73d90168774e7aa6d3b61f6d0f2cd7a677376ae17a78b9187114a
|
|
@@ -3,7 +3,7 @@ mumuki.load(function () {
|
|
|
3
3
|
if(!$bubble.length) return;
|
|
4
4
|
|
|
5
5
|
var availableTabs = ['.description', '.hint'];
|
|
6
|
-
var $speechParagraphs, paragraphHeight, scrollHeight;
|
|
6
|
+
var $speechParagraphs, paragraphHeight, scrollHeight, nextSpeechBlinking;
|
|
7
7
|
var currentParagraphIndex = 0;
|
|
8
8
|
var paragraphCount = 1;
|
|
9
9
|
var paragraphsLines = 2;
|
|
@@ -53,8 +53,13 @@ mumuki.load(function () {
|
|
|
53
53
|
})
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
+
if(paragraphCount > 1) {
|
|
57
|
+
nextSpeechBlinking = setInterval(() => $nextSpeech.fadeTo('slow', 0.1).fadeTo('slow', 1.0), 1000);
|
|
58
|
+
}
|
|
59
|
+
|
|
56
60
|
$nextSpeech.click(function () {
|
|
57
61
|
showParagraph(currentParagraphIndex + 1);
|
|
62
|
+
clearInterval(nextSpeechBlinking);
|
|
58
63
|
});
|
|
59
64
|
|
|
60
65
|
$prevSpeech.click(function () {
|
|
@@ -24,12 +24,10 @@ module OrganizationsControllerTemplate
|
|
|
24
24
|
params
|
|
25
25
|
.require(:organization)
|
|
26
26
|
.permit(:book,
|
|
27
|
-
:
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
:theme_stylesheet, :extension_javascript, :terms_of_service,
|
|
32
|
-
:community_link, :login_provider, :embeddable,
|
|
27
|
+
:name,
|
|
28
|
+
*Mumukit::Platform::Organization::Profile.attributes,
|
|
29
|
+
*Mumukit::Platform::Organization::Theme.attributes,
|
|
30
|
+
*(Mumukit::Platform::Organization::Settings.attributes - [:login_methods]),
|
|
33
31
|
login_methods: [])
|
|
34
32
|
.tap { |it| it.merge!(book: Book.find_by!(slug: it[:book])) if it[:book] }
|
|
35
33
|
end
|
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
class GuideProgressController < ApplicationController
|
|
2
|
+
before_action :check_resettable!, only: :destroy
|
|
3
|
+
|
|
2
4
|
def destroy
|
|
3
|
-
guide = Guide.find(params[:guide_id])
|
|
4
5
|
guide.clear_progress! current_user
|
|
5
6
|
redirect_back fallback_location: url_for(guide)
|
|
6
7
|
end
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def check_resettable!
|
|
12
|
+
raise Mumuki::Laboratory::ForbiddenError unless guide.resettable?
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def guide
|
|
16
|
+
@guide ||= Guide.find(params[:guide_id])
|
|
17
|
+
end
|
|
7
18
|
end
|
data/app/models/book.rb
CHANGED
|
@@ -27,7 +27,7 @@ class Book < Content
|
|
|
27
27
|
self.description = json['description'].squeeze(' ')
|
|
28
28
|
|
|
29
29
|
rebuild! json['chapters'].map { |it| Topic.find_by!(slug: it).as_chapter_of(self) }
|
|
30
|
-
rebuild_complements! (json['complements']||[]).map { |it| Guide.find_by
|
|
30
|
+
rebuild_complements! (json['complements']||[]).map { |it| Guide.find_by(slug: it)&.as_complement_of(self) }.compact
|
|
31
31
|
|
|
32
32
|
Organization.all.each { |org| org.reindex_usages! }
|
|
33
33
|
end
|
|
@@ -35,8 +35,8 @@ module GuideContainer
|
|
|
35
35
|
|
|
36
36
|
# Tells if this guide container
|
|
37
37
|
# imposes any kind of limit to the number of submission
|
|
38
|
-
# to its assignments
|
|
39
|
-
def
|
|
38
|
+
# to its assignments, which may depend on the exercise's type
|
|
39
|
+
def limited_for?(exercise)
|
|
40
40
|
false
|
|
41
41
|
end
|
|
42
42
|
|
|
@@ -46,4 +46,8 @@ module GuideContainer
|
|
|
46
46
|
|
|
47
47
|
def start!(user)
|
|
48
48
|
end
|
|
49
|
+
|
|
50
|
+
def resettable?
|
|
51
|
+
true
|
|
52
|
+
end
|
|
49
53
|
end
|
data/app/models/exam.rb
CHANGED
|
@@ -128,16 +128,20 @@ class Exam < ApplicationRecord
|
|
|
128
128
|
end
|
|
129
129
|
|
|
130
130
|
def attempts_left_for(assignment)
|
|
131
|
-
max_attempts_for(assignment) - (assignment.attempts_count || 0)
|
|
131
|
+
max_attempts_for(assignment.exercise) - (assignment.attempts_count || 0)
|
|
132
132
|
end
|
|
133
133
|
|
|
134
|
-
def
|
|
135
|
-
|
|
134
|
+
def limited_for?(exercise)
|
|
135
|
+
max_attempts_for(exercise).present?
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def resettable?
|
|
139
|
+
false
|
|
136
140
|
end
|
|
137
141
|
|
|
138
142
|
private
|
|
139
143
|
|
|
140
|
-
def max_attempts_for(
|
|
141
|
-
|
|
144
|
+
def max_attempts_for(exercise)
|
|
145
|
+
exercise.choice? ? max_choice_submissions : max_problem_submissions
|
|
142
146
|
end
|
|
143
147
|
end
|
data/app/models/exercise.rb
CHANGED
|
@@ -22,7 +22,7 @@ class Exercise < ApplicationRecord
|
|
|
22
22
|
:guide
|
|
23
23
|
|
|
24
24
|
randomize :description, :hint, :extra, :test, :default_content
|
|
25
|
-
delegate :
|
|
25
|
+
delegate :timed?, to: :navigable_parent
|
|
26
26
|
|
|
27
27
|
def console?
|
|
28
28
|
queriable?
|
|
@@ -158,6 +158,10 @@ class Exercise < ApplicationRecord
|
|
|
158
158
|
end
|
|
159
159
|
end
|
|
160
160
|
|
|
161
|
+
def limited?
|
|
162
|
+
navigable_parent.limited_for?(self)
|
|
163
|
+
end
|
|
164
|
+
|
|
161
165
|
private
|
|
162
166
|
|
|
163
167
|
def evaluation_class
|
data/app/models/guide.rb
CHANGED
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
</div>
|
|
37
37
|
|
|
38
38
|
<h3 style="display:inline;"><%= t :content %></h3>
|
|
39
|
-
<%= restart_guide_link(@guide) if current_user && @guide.started?(current_user) %>
|
|
39
|
+
<%= restart_guide_link(@guide) if current_user && @guide.started?(current_user) && @guide.resettable? %>
|
|
40
40
|
<%= render partial: 'layouts/progress_listing', locals: {exercises: @guide.exercises} %>
|
|
41
41
|
|
|
42
42
|
<% if @stats.try :done? %>
|
data/lib/mumuki/laboratory.rb
CHANGED
|
@@ -14,11 +14,11 @@ require 'mumukit/randomizer'
|
|
|
14
14
|
require 'mumukit/inspection'
|
|
15
15
|
require 'mumukit/bridge'
|
|
16
16
|
require 'mumukit/content_type'
|
|
17
|
+
require 'mumukit/directives'
|
|
17
18
|
require 'kaminari'
|
|
18
19
|
require 'bootstrap-kaminari-views'
|
|
19
20
|
|
|
20
21
|
require_relative './laboratory/mumukit/auth'
|
|
21
|
-
require_relative './laboratory/mumukit/directives'
|
|
22
22
|
require_relative './laboratory/mumukit/login'
|
|
23
23
|
require_relative './laboratory/mumukit/nuntius'
|
|
24
24
|
require_relative './laboratory/mumukit/platform'
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe GuideProgressController, organization_workspace: :test do
|
|
4
|
+
let(:user) { create(:user) }
|
|
5
|
+
|
|
6
|
+
let(:exam_problem) { build(:problem) }
|
|
7
|
+
let(:test_organization) { Organization.find_by_name('test') }
|
|
8
|
+
let(:exam) { create(:exam, organization: test_organization, guide: guide) }
|
|
9
|
+
let(:guide) { create(:guide, exercises: [exam_problem])}
|
|
10
|
+
|
|
11
|
+
before { exam.index_usage! test_organization }
|
|
12
|
+
|
|
13
|
+
before { reindex_current_organization! }
|
|
14
|
+
|
|
15
|
+
describe 'delete' do
|
|
16
|
+
before { set_current_user! user }
|
|
17
|
+
before { delete :destroy, params: { guide_id: guide.id } }
|
|
18
|
+
|
|
19
|
+
it { expect(response.status).to eq 403 }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -5,12 +5,14 @@ feature 'Exams Flow', organization_workspace: :test do
|
|
|
5
5
|
let(:other_exam) { create(:exam, organization: other_organization) }
|
|
6
6
|
let!(:exam_not_in_path) { create :exam }
|
|
7
7
|
|
|
8
|
-
let!(:chapter) {
|
|
9
|
-
create(:chapter, lessons: [
|
|
10
|
-
create(:lesson)]) }
|
|
11
|
-
|
|
12
8
|
let(:other_organization) { create(:organization, name: 'baz') }
|
|
13
9
|
|
|
10
|
+
let(:exam_with_no_submission_limits) { create(:exam, organization_id: Organization.find_by_name('test').id, guide_id: guide.id) }
|
|
11
|
+
let(:guide) { create(:guide, exercises: [exercise])}
|
|
12
|
+
let(:exercise) { create(:exercise, name: 'Exam Exercise') }
|
|
13
|
+
|
|
14
|
+
before { exam_with_no_submission_limits.index_usage! Organization.find_by_name('test') }
|
|
15
|
+
|
|
14
16
|
before { reindex_current_organization! }
|
|
15
17
|
|
|
16
18
|
context 'inexistent exam' do
|
|
@@ -51,4 +53,13 @@ feature 'Exams Flow', organization_workspace: :test do
|
|
|
51
53
|
|
|
52
54
|
expect(page).to have_text('This exam is no longer available.')
|
|
53
55
|
end
|
|
56
|
+
|
|
57
|
+
scenario 'visit exercise for exam with no submission limits' do
|
|
58
|
+
user = create(:user)
|
|
59
|
+
set_current_user! user
|
|
60
|
+
exam_with_no_submission_limits.authorize!(user)
|
|
61
|
+
visit "/exercises/#{exercise.id}"
|
|
62
|
+
|
|
63
|
+
expect(page).to have_text('Exam Exercise')
|
|
64
|
+
end
|
|
54
65
|
end
|
|
@@ -3,12 +3,19 @@ require 'spec_helper'
|
|
|
3
3
|
feature 'Exercise Flow', organization_workspace: :test do
|
|
4
4
|
let(:user) { create(:user) }
|
|
5
5
|
|
|
6
|
-
let!(:problem) { build(:problem)}
|
|
6
|
+
let!(:problem) { build(:problem) }
|
|
7
7
|
|
|
8
8
|
let!(:lesson) { create(:lesson, description: 'An awesome guide', exercises: [problem]) }
|
|
9
9
|
|
|
10
10
|
let!(:chapter) { create(:chapter, lessons: [lesson]) }
|
|
11
11
|
|
|
12
|
+
let(:exam_problem) { build(:problem) }
|
|
13
|
+
let(:test_organization) { Organization.find_by_name('test') }
|
|
14
|
+
let(:exam) { create(:exam, organization: test_organization, guide: guide) }
|
|
15
|
+
let(:guide) { create(:guide, exercises: [exam_problem])}
|
|
16
|
+
|
|
17
|
+
before { exam.index_usage! test_organization }
|
|
18
|
+
|
|
12
19
|
before { reindex_current_organization! }
|
|
13
20
|
|
|
14
21
|
let(:restart_xpath) { "//i[@title='#{I18n.t(:restart)}']" }
|
|
@@ -40,5 +47,14 @@ feature 'Exercise Flow', organization_workspace: :test do
|
|
|
40
47
|
expect(page).to have_text('An awesome guide')
|
|
41
48
|
expect(page).to have_xpath(restart_xpath)
|
|
42
49
|
end
|
|
50
|
+
|
|
51
|
+
scenario 'visit exam guide with solutions sent for it' do
|
|
52
|
+
exam_problem.submit_solution! user
|
|
53
|
+
exam.authorize!(user)
|
|
54
|
+
|
|
55
|
+
visit "/exams/#{exam.id}"
|
|
56
|
+
|
|
57
|
+
expect(page).to_not have_xpath(restart_xpath)
|
|
58
|
+
end
|
|
43
59
|
end
|
|
44
60
|
end
|
|
@@ -18,22 +18,32 @@ describe Book, organization_workspace: :test do
|
|
|
18
18
|
slug: 'mumuki/mumuki-sample-book',
|
|
19
19
|
locale: 'en',
|
|
20
20
|
chapters: [topic_1.slug, topic_2.slug],
|
|
21
|
-
complements:
|
|
21
|
+
complements: complement_slugs,
|
|
22
22
|
}.deep_stringify_keys
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
describe '#import_from_json!' do
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
context 'when complements are present' do
|
|
27
|
+
let(:complement_slugs) { [guide_2.slug, guide_1.slug] }
|
|
28
|
+
|
|
29
|
+
before { book.import_from_json!(book_json) }
|
|
30
|
+
|
|
31
|
+
it { expect(book.name).to eq 'sample book' }
|
|
32
|
+
it { expect(book.description).to eq 'a sample book description' }
|
|
33
|
+
it { expect(book.locale).to eq 'en' }
|
|
34
|
+
it { expect(book.chapters.count).to eq 2 }
|
|
35
|
+
it { expect(book.complements.count).to eq 2 }
|
|
36
|
+
|
|
37
|
+
it { expect(topic_2.reload.usage_in_organization).to be_a Chapter }
|
|
38
|
+
it { expect(guide_2.reload.usage_in_organization).to be_a Complement }
|
|
28
39
|
end
|
|
29
40
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
it { expect(book.locale).to eq 'en' }
|
|
33
|
-
it { expect(book.chapters.count).to eq 2 }
|
|
34
|
-
it { expect(book.complements.count).to eq 2 }
|
|
41
|
+
context 'when complements are not present' do
|
|
42
|
+
let(:complement_slugs) { ['foo/bar', guide_1.slug] }
|
|
35
43
|
|
|
36
|
-
|
|
37
|
-
|
|
44
|
+
before { book.import_from_json!(book_json) }
|
|
45
|
+
|
|
46
|
+
it { expect(book.complements.count).to eq 1 }
|
|
47
|
+
end
|
|
38
48
|
end
|
|
39
49
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mumuki-laboratory
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.
|
|
4
|
+
version: 5.10.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Franco Bulgarelli
|
|
@@ -86,14 +86,14 @@ dependencies:
|
|
|
86
86
|
requirements:
|
|
87
87
|
- - "~>"
|
|
88
88
|
- !ruby/object:Gem::Version
|
|
89
|
-
version: '3.
|
|
89
|
+
version: '3.5'
|
|
90
90
|
type: :runtime
|
|
91
91
|
prerelease: false
|
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
93
|
requirements:
|
|
94
94
|
- - "~>"
|
|
95
95
|
- !ruby/object:Gem::Version
|
|
96
|
-
version: '3.
|
|
96
|
+
version: '3.5'
|
|
97
97
|
- !ruby/object:Gem::Dependency
|
|
98
98
|
name: mumukit-nuntius
|
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -114,42 +114,42 @@ dependencies:
|
|
|
114
114
|
requirements:
|
|
115
115
|
- - "~>"
|
|
116
116
|
- !ruby/object:Gem::Version
|
|
117
|
-
version: '2.
|
|
117
|
+
version: '2.6'
|
|
118
118
|
type: :runtime
|
|
119
119
|
prerelease: false
|
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
|
121
121
|
requirements:
|
|
122
122
|
- - "~>"
|
|
123
123
|
- !ruby/object:Gem::Version
|
|
124
|
-
version: '2.
|
|
124
|
+
version: '2.6'
|
|
125
125
|
- !ruby/object:Gem::Dependency
|
|
126
126
|
name: mumukit-login
|
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
|
128
128
|
requirements:
|
|
129
129
|
- - "~>"
|
|
130
130
|
- !ruby/object:Gem::Version
|
|
131
|
-
version: '6.
|
|
131
|
+
version: '6.1'
|
|
132
132
|
type: :runtime
|
|
133
133
|
prerelease: false
|
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
|
135
135
|
requirements:
|
|
136
136
|
- - "~>"
|
|
137
137
|
- !ruby/object:Gem::Version
|
|
138
|
-
version: '6.
|
|
138
|
+
version: '6.1'
|
|
139
139
|
- !ruby/object:Gem::Dependency
|
|
140
140
|
name: mumukit-directives
|
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
|
142
142
|
requirements:
|
|
143
143
|
- - "~>"
|
|
144
144
|
- !ruby/object:Gem::Version
|
|
145
|
-
version: '0.
|
|
145
|
+
version: '0.5'
|
|
146
146
|
type: :runtime
|
|
147
147
|
prerelease: false
|
|
148
148
|
version_requirements: !ruby/object:Gem::Requirement
|
|
149
149
|
requirements:
|
|
150
150
|
- - "~>"
|
|
151
151
|
- !ruby/object:Gem::Version
|
|
152
|
-
version: '0.
|
|
152
|
+
version: '0.5'
|
|
153
153
|
- !ruby/object:Gem::Dependency
|
|
154
154
|
name: mumukit-assistant
|
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -838,7 +838,6 @@ files:
|
|
|
838
838
|
- lib/mumuki/laboratory/locales/pt.yml
|
|
839
839
|
- lib/mumuki/laboratory/locales/views.es.yml
|
|
840
840
|
- lib/mumuki/laboratory/mumukit/auth.rb
|
|
841
|
-
- lib/mumuki/laboratory/mumukit/directives.rb
|
|
842
841
|
- lib/mumuki/laboratory/mumukit/login.rb
|
|
843
842
|
- lib/mumuki/laboratory/mumukit/nuntius.rb
|
|
844
843
|
- lib/mumuki/laboratory/mumukit/platform.rb
|
|
@@ -891,6 +890,7 @@ files:
|
|
|
891
890
|
- spec/controllers/courses_api_controller_spec.rb
|
|
892
891
|
- spec/controllers/discussions_controller_spec.rb
|
|
893
892
|
- spec/controllers/exercise_solutions_controller_spec.rb
|
|
893
|
+
- spec/controllers/guide_progress_controller_spec.rb
|
|
894
894
|
- spec/controllers/messages_controller_spec.rb
|
|
895
895
|
- spec/controllers/organizations_api_controller_spec.rb
|
|
896
896
|
- spec/controllers/students_api_controller_spec.rb
|
|
@@ -1078,6 +1078,7 @@ test_files:
|
|
|
1078
1078
|
- spec/controllers/courses_api_controller_spec.rb
|
|
1079
1079
|
- spec/controllers/discussions_controller_spec.rb
|
|
1080
1080
|
- spec/controllers/exercise_solutions_controller_spec.rb
|
|
1081
|
+
- spec/controllers/guide_progress_controller_spec.rb
|
|
1081
1082
|
- spec/controllers/messages_controller_spec.rb
|
|
1082
1083
|
- spec/controllers/organizations_api_controller_spec.rb
|
|
1083
1084
|
- spec/controllers/students_api_controller_spec.rb
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
require 'mumukit/directives'
|
|
2
|
-
|
|
3
|
-
class Mumukit::Directives::Sections < Mumukit::Directives::Directive
|
|
4
|
-
# TODO Move this behaviour to gem
|
|
5
|
-
def join(sections)
|
|
6
|
-
file_declarations, file_references = sections.map do |section, content|
|
|
7
|
-
[build(section, content), interpolate(section)]
|
|
8
|
-
end.transpose
|
|
9
|
-
|
|
10
|
-
file_declarations.join "\n"
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def build(section, content)
|
|
14
|
-
"#{comment_type.comment "<#{section}#"}#{content}#{comment_type.comment "##{section}>"}"
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def interpolate(section)
|
|
18
|
-
comment_type.comment("...#{section}...")
|
|
19
|
-
end
|
|
20
|
-
end
|