mumuki-laboratory 5.9.1 → 5.10.0

Sign up to get free protection for your applications and to get access to all the features.
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