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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 91bec7b08d66fabf07b739d2619d5ef4a572d9c413a9ef57ebf6e412a5b7bc65
4
- data.tar.gz: 1684b4fb366fb66d0ed7bb5c4efd08c7fedd694432d2b1a89357b6938eb81938
3
+ metadata.gz: d5fa7aa2c319aee6bd932340664e1ed91d87d3dcee8f9b8f429301a7e02b2526
4
+ data.tar.gz: d12b201ddb14ba04dfbe233d5e57fcec1b57260c85a622243ed03de1226550d2
5
5
  SHA512:
6
- metadata.gz: 9f5b9a1ae38fcf0ebf7ec66dec7d118fe451a653aa8519d57c60f9240bb2211a8c4a51a5c66161319402bcbf91a877603d694896bc6dfe9cd7ba01ef4ac75357
7
- data.tar.gz: c503b00c9256d9d24e5916ddd62816e80d650ec8aae256263e533d3f002787159de29fcc58486defb68206581197f5a5341d8bb5cf793cdef82be068ec9e4981
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
- :contact_email, :name, :locale, :description,
28
- :logo_url, :banner_url, :open_graph_image_url, :favicon_url, :breadcrumb_image_url,
29
- :raise_hand_enabled, :report_issue_enabled, :forum_enabled,
30
- :feedback_suggestions_enabled, :public, :immersive,
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,4 +1,4 @@
1
- class DiscussionsController < AjaxController
1
+ class DiscussionsController < ApplicationController
2
2
  include Mumuki::Laboratory::Controllers::Content
3
3
 
4
4
  before_action :validate_forum_enabled!
@@ -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!(slug: it).as_complement_of(self) }
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 limited?
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 limited?
135
- true
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(assignment)
141
- assignment.choice? ? max_choice_submissions : max_problem_submissions
144
+ def max_attempts_for(exercise)
145
+ exercise.choice? ? max_choice_submissions : max_problem_submissions
142
146
  end
143
147
  end
@@ -22,7 +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
+ 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
@@ -86,4 +86,7 @@ class Guide < Content
86
86
  book.complements.find_by(guide_id: id) || Complement.new(guide: self, book: book)
87
87
  end
88
88
 
89
+ def resettable?
90
+ usage_in_organization.resettable?
91
+ end
89
92
  end
@@ -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? %>
@@ -1,5 +1,5 @@
1
1
  module Mumuki
2
2
  module Laboratory
3
- VERSION = '5.9.1'
3
+ VERSION = '5.10.0'
4
4
  end
5
5
  end
@@ -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: [guide_2.slug, guide_1.slug]
21
+ complements: complement_slugs,
22
22
  }.deep_stringify_keys
23
23
  end
24
24
 
25
25
  describe '#import_from_json!' do
26
- before do
27
- book.import_from_json!(book_json)
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
- it { expect(book.name).to eq 'sample book' }
31
- it { expect(book.description).to eq 'a sample book description' }
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
- it { expect(topic_2.reload.usage_in_organization).to be_a Chapter }
37
- it { expect(guide_2.reload.usage_in_organization).to be_a Complement }
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.9.1
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.4'
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.4'
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.4'
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.4'
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.0'
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.0'
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.3'
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.3'
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