mumuki-laboratory 9.18.1 → 9.21.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/mumuki_laboratory/application/timer.js +2 -5
  3. data/app/assets/stylesheets/mumuki_laboratory/application/_layout.scss +0 -8
  4. data/app/assets/stylesheets/mumuki_laboratory/application/modules/_user_profile.scss +1 -9
  5. data/app/assets/stylesheets/mumuki_laboratory/application.scss +0 -1
  6. data/app/controllers/appendixes_controller.rb +7 -0
  7. data/app/controllers/application_controller.rb +6 -2
  8. data/app/controllers/complements_controller.rb +13 -0
  9. data/app/helpers/application_helper.rb +3 -1
  10. data/app/helpers/discussions_helper.rb +2 -2
  11. data/app/helpers/profile_helper.rb +1 -1
  12. data/app/views/book/show.html.erb +21 -18
  13. data/app/views/chapters/show.html.erb +1 -1
  14. data/app/views/discussions/_message.html.erb +1 -1
  15. data/app/views/errors/forbidden.html.erb +1 -3
  16. data/app/views/errors/gone.html.erb +1 -2
  17. data/app/views/errors/not_found.html.erb +1 -1
  18. data/app/views/exercises/show.html.erb +1 -1
  19. data/app/views/guides/_guide.html.erb +10 -6
  20. data/app/views/layouts/_timer.html.erb +1 -1
  21. data/app/views/users/_basic_profile_fields.html.erb +20 -16
  22. data/app/views/users/_edit_user_form.html.erb +9 -7
  23. data/lib/mumuki/laboratory/controllers/dynamic_errors.rb +6 -6
  24. data/lib/mumuki/laboratory/controllers/validate_access_mode.rb +5 -1
  25. data/lib/mumuki/laboratory/locales/es-CL.yml +1 -1
  26. data/lib/mumuki/laboratory/locales/es.yml +1 -1
  27. data/lib/mumuki/laboratory/version.rb +1 -1
  28. data/spec/dummy/db/schema.rb +2 -1
  29. data/spec/features/guides_flow_spec.rb +29 -7
  30. data/spec/features/immersive_redirection_spec.rb +1 -1
  31. data/spec/features/not_found_public_flow_spec.rb +8 -1
  32. data/spec/features/profile_flow_spec.rb +1 -1
  33. data/spec/features/read_only_flow_spec.rb +86 -17
  34. metadata +4 -5
  35. data/app/assets/stylesheets/mumuki_laboratory/application/_alerts.scss +0 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1ae29f9665d934e9d1a3ef71a67074980155c36eabd6dc0ee5d66ddcc7c6cea3
4
- data.tar.gz: 5a9158cd4bec4155c1890241f2dab4d87889c87d0b9143d572bc63cc4e925f99
3
+ metadata.gz: e520213e1d2d2cd643b0ee1d9506324394a18978ea319f8f0c7917a1bfba8c2b
4
+ data.tar.gz: a8e99258a755b5fbe2a2f3b4927b1102ebbcee837e995839bf1989324ff783af
5
5
  SHA512:
6
- metadata.gz: 9cafbd64b3e29e4005bb51c3da4a3e45a7532295a34e4e7727185af580544ca9ff614f0c7b15496f9a043e6dd9c8d3becc82282a9f4deb6cbfec6c3e09fa78ed
7
- data.tar.gz: ecf6beb36af7772043cb3775523373b38db0cdbc58ebe316b9fd4239decf8610c9ea7200b1fc4954be83765a2f71a1d97e75621814c19dcccb5984a1ac4f17ec
6
+ metadata.gz: 51562ad86aab9f22e3c6ae6a651bdc22cdd84159eb9394122b12ff885f19ceac51b375544e852870a3c80ec689fd5bf98e9291c8797dddf444887269cb006ddc
7
+ data.tar.gz: eb8b795ebfdd92ccb48d571558c5cba3866fb09ce03217f8a5a22cd82799a5bec70bcf97e8aa71789ca21d2d484c0ae928db7aa3b9fb955bb573d57af896cbcc
@@ -1,9 +1,6 @@
1
1
  mumuki.startTimer = (() => {
2
- function startTimer(endDate) {
3
- var endTime = new Date(endDate).getTime();
4
- var currentTime = Date.now();
5
- var diffTime = endTime - currentTime;
6
- var duration = moment.duration(diffTime, 'milliseconds');
2
+ function startTimer(seconds) {
3
+ var duration = moment.duration(seconds, 'seconds');
7
4
  var intervalDuration = 1000;
8
5
 
9
6
  var interval = mumuki.setInterval(function () {
@@ -34,14 +34,6 @@
34
34
  background-image: none;
35
35
  }
36
36
 
37
- .alert {
38
- margin: 10px 50px 10px 15px;
39
- }
40
-
41
- .alert-danger a {
42
- color: greenyellow;
43
- }
44
-
45
37
  .exercise-search-box {
46
38
  width: 263px;
47
39
  position: fixed
@@ -7,6 +7,7 @@
7
7
 
8
8
  .mu-profile-actions {
9
9
  margin-left: auto;
10
+ text-align: center;
10
11
 
11
12
  .btn {
12
13
  width: 150px;
@@ -18,15 +19,6 @@
18
19
  border-color: lighten($mu-color-complementary, 15%);
19
20
  }
20
21
  }
21
-
22
- &.mobile {
23
- text-align: center;
24
- margin: 20px 5px -20px -10px;
25
-
26
- .btn {
27
- width: calc(50% - 20px);
28
- }
29
- }
30
22
  }
31
23
 
32
24
  .mu-profile-info {
@@ -13,5 +13,4 @@ $da-font-path: asset-path('assets');
13
13
  @import "application/invitations";
14
14
  @import "application/modules";
15
15
  @import "application/errors";
16
- @import "application/alerts";
17
16
  @import "application/codemirror-themes";
@@ -1,5 +1,12 @@
1
1
  class AppendixesController < ApplicationController
2
+
2
3
  def show
3
4
  @chapter = Chapter.find(params[:chapter_id])
4
5
  end
6
+
7
+ private
8
+
9
+ def authorization_minimum_role
10
+ :ex_student
11
+ end
5
12
  end
@@ -44,7 +44,8 @@ class ApplicationController < ActionController::Base
44
44
  :theme_stylesheet_url,
45
45
  :extension_javascript_url,
46
46
  :current_immersive_path,
47
- :current_access_mode
47
+ :current_access_mode,
48
+ :limited_query?
48
49
 
49
50
  add_flash_types :info
50
51
 
@@ -129,7 +130,6 @@ class ApplicationController < ActionController::Base
129
130
  def validate_user_profile!
130
131
  unless current_user.profile_completed?
131
132
  save_location_before! :profile_completion
132
- flash[:info] = I18n.t :please_fill_profile_data
133
133
  redirect_to edit_user_path
134
134
  end
135
135
  end
@@ -171,4 +171,8 @@ class ApplicationController < ActionController::Base
171
171
  current_user.restore_organization_progress!(Organization.current)
172
172
  end
173
173
  end
174
+
175
+ def limited_query?
176
+ params[:limit].present?
177
+ end
174
178
  end
@@ -1,8 +1,21 @@
1
1
  class ComplementsController < GuideContainerController
2
+ include Mumuki::Laboratory::Controllers::ValidateAccessMode
2
3
 
3
4
  private
4
5
 
5
6
  def subject
6
7
  @complement ||= Complement.find_by(id: params[:id])
7
8
  end
9
+
10
+ def authorization_minimum_role
11
+ :ex_student
12
+ end
13
+
14
+ def subject_container
15
+ subject.guide
16
+ end
17
+
18
+ def contentless_subject?
19
+ subject_container.exercises.empty?
20
+ end
8
21
  end
@@ -17,7 +17,9 @@ module ApplicationHelper
17
17
  end
18
18
 
19
19
  def paginate(object, options = {})
20
- super(object, {theme: 'bootstrap-5', pagination_class: 'flex-wrap justify-content-center'}.merge(options))
20
+ unless limited_query?
21
+ super(object, {theme: 'bootstrap-5', pagination_class: 'flex-wrap justify-content-center'}.merge(options))
22
+ end
21
23
  end
22
24
 
23
25
  def last_box_class(trailing_boxes)
@@ -38,14 +38,14 @@ module DiscussionsHelper
38
38
 
39
39
  def solve_discussion_params_for(user)
40
40
  if user&.moderator_here?
41
- {status: :pending_review, sort: :responses_count_asc, requires_moderator_response: true}
41
+ {status: :pending_review, sort: :responses_count_asc, requires_attention: true}
42
42
  else
43
43
  {status: :opened, sort: :responses_count_desc}
44
44
  end
45
45
  end
46
46
 
47
47
  def default_discussions_params
48
- {status: :solved, sort: :upvotes_count_desc}
48
+ {status: :solved, sort: :created_at_desc, recent: true, limit: 15}
49
49
  end
50
50
 
51
51
  def user_avatar(user, image_class='')
@@ -4,7 +4,7 @@ module ProfileHelper
4
4
  end
5
5
 
6
6
  def cancel_edit_profile_button
7
- link_to t(:cancel), :user, class: 'btn btn-secondary' if current_user.profile_completed?
7
+ link_to t(:cancel), :user, class: 'btn btn-outline-complementary' if current_user.profile_completed?
8
8
  end
9
9
 
10
10
  def save_edit_profile_button(form)
@@ -17,30 +17,33 @@
17
17
  <% end %>
18
18
  <% end %>
19
19
 
20
- <% if show_content?(@book) %>
21
- <h2><%= t(:chapters) %></h2>
22
- <% end %>
20
+ <% unless @book.chapters.empty? %>
21
+ <% if show_content?(@book) %>
22
+ <h2><%= t(:chapters) %></h2>
23
+ <% end %>
23
24
 
24
- <% @book.chapter_visibilities_in(current_workspace).each do |it, enabled| %>
25
+ <% @book.chapter_visibilities_in(current_workspace).each do |it, enabled| %>
25
26
 
26
- <% next unless show_content?(it.topic) %>
27
+ <% next unless show_content?(it.topic) %>
27
28
 
28
- <div class="chapter-container">
29
- <div class="chapter <%= enabled ? '' : 'mu-locked' %>">
30
- <h3><%= it.number %>. <%= link_to_path_element it, mode: :plain %></h3>
31
- <div class="text-box" <%= 'aria-label=""' unless enabled %>>
32
- <%= it.description_teaser_html %>
29
+ <div class="chapter-container">
30
+ <div class="chapter <%= enabled ? '' : 'mu-locked' %>">
31
+ <h3><%= it.number %>. <%= link_to_path_element it, mode: :plain %></h3>
32
+ <div class="text-box" <%= 'aria-label=""' unless enabled %>>
33
+ <%= it.description_teaser_html %>
34
+ </div>
33
35
  </div>
34
- </div>
35
36
 
36
- <% unless enabled %>
37
- <div class="text-center mu-lock">
38
- <i class="fas fa-lock fa-5x"></i>
39
- <p><%= t :locked_content %></p>
40
- </div>
41
- <% end %>
42
- </div>
37
+ <% unless enabled %>
38
+ <div class="text-center mu-lock">
39
+ <i class="fas fa-lock fa-5x"></i>
40
+ <p><%= t :locked_content %></p>
41
+ </div>
42
+ <% end %>
43
+ </div>
44
+ <% end %>
43
45
  <% end %>
46
+
44
47
  <% if current_user? && @exams.present? %>
45
48
  <h2><%= t(:exams) %></h2>
46
49
  <% @exams.each_with_index do |it, index| %>
@@ -20,7 +20,7 @@
20
20
  </div>
21
21
  </div>
22
22
 
23
- <% if @chapter.lessons.present? %>
23
+ <% unless @chapter.lessons.empty? %>
24
24
  <% if @chapter.monolesson? %>
25
25
  <div> <%= render partial: 'guides/guide', locals: { subject: @monolesson } %> </div>
26
26
  <% else %>
@@ -3,7 +3,7 @@
3
3
  <div class="discussion-message-bubble-header">
4
4
  <div class="discussion-message-bubble-title">
5
5
  <%= linked_discussion_user_name user %>
6
- <% if user.moderator_here? %>
6
+ <% if message.from_moderator? %>
7
7
  <span class="moderator-badge"><%= t(:moderation) %></span>
8
8
  <% end %>
9
9
  <span class="message-date">
@@ -8,12 +8,10 @@
8
8
  <%= t(:error_description, error: link_to_status_codes(403)).html_safe %>
9
9
  </p>
10
10
  <p class="mu-error-explanation-line">
11
- <%= Organization.current.explain_error(403, explanation).html_safe %>
11
+ <%= Organization.current.explain_error(error_code, explanation).html_safe %>
12
12
  </p>
13
13
  <p class="mu-error-contact-line mu-maybe">
14
14
  <%= t(:contact_administrator, link: mail_to_administrator).html_safe %>
15
15
  </p>
16
16
  </div>
17
17
  <% end %>
18
-
19
-
@@ -8,8 +8,7 @@
8
8
  <%= t(:error_description, error: link_to_status_codes(410)).html_safe %>
9
9
  </p>
10
10
  <p class="mu-error-explanation-line">
11
- <%= Organization.current.explain_error(410, explanation).html_safe %>
11
+ <%= Organization.current.explain_error(error_code, explanation).html_safe %>
12
12
  </p>
13
13
  </div>
14
14
  <% end %>
15
-
@@ -8,7 +8,7 @@
8
8
  <%= t(:error_description, error: link_to_error_404).html_safe %>
9
9
  </p>
10
10
  <p class="mu-error-explanation-line">
11
- <%= Organization.current.explain_error(404, :not_found_explanation).html_safe %>
11
+ <%= Organization.current.explain_error(:not_found, :not_found_explanation).html_safe %>
12
12
  </p>
13
13
  </div>
14
14
  <% end %>
@@ -9,7 +9,7 @@
9
9
  <% @stats = @exercise.stats_for(current_user) %>
10
10
 
11
11
  <% if @exercise.navigable_parent.timed? && !current_user.teacher? %>
12
- <%= render partial: 'layouts/timer', locals: {end_time: @exercise.navigable_parent.real_end_time(current_user)} %>
12
+ <%= render partial: 'layouts/timer', locals: { duration: @exercise.navigable_parent.time_left(current_user) } %>
13
13
  <% end %>
14
14
 
15
15
  <% unless @exercise.input_kids? %>
@@ -3,17 +3,21 @@
3
3
  <%= render partial: 'layouts/authoring', locals: {guide: @guide} %>
4
4
 
5
5
  <% if subject.timed? && @stats.started? && !current_user.teacher? %>
6
- <%= render partial: 'layouts/timer', locals: {end_time: subject.real_end_time(current_user)} %>
6
+ <%= render partial: 'layouts/timer', locals: { duration: subject.time_left(current_user) } %>
7
7
  <% end %>
8
8
 
9
9
  <%= yield if block_given? %>
10
10
 
11
- <h3>
12
- <%= t :exercises %>
13
- <%= restart_guide_link(@guide) if current_user && @stats.started? && @guide.resettable? %>
14
- </h3>
11
+ <% unless @guide.exercises.empty? %>
15
12
 
16
- <%= render partial: 'layouts/progress_listing', locals: { guide: @guide } %>
13
+ <h3>
14
+ <%= t :exercises %>
15
+ <%= restart_guide_link(@guide) if current_user && @stats.started? && @guide.resettable? %>
16
+ </h3>
17
+
18
+ <%= render partial: 'layouts/progress_listing', locals: { guide: @guide } %>
19
+
20
+ <% end %>
17
21
 
18
22
  <% if @stats&.done? %>
19
23
  <div class="text-box">
@@ -4,7 +4,7 @@
4
4
  <i class="timer timer-text text-center"><%= t :time_left %></i>
5
5
  <i id="timer" class="timer timer-text text-center"></i>
6
6
  <script>
7
- mumuki.startTimer('<%= end_time&.iso8601 %>');
7
+ mumuki.startTimer(<%= duration %>);
8
8
  </script>
9
9
  </div>
10
10
  </div>
@@ -1,19 +1,23 @@
1
- <fieldset>
2
- <div><%= form.label(t :first_name) %></div>
3
- <div><%= form.text_field :first_name, required: true, class: 'form-control' %></div>
4
- </fieldset>
5
- <fieldset>
6
- <div><%= form.label(t :last_name) %></div>
7
- <div><%= form.text_field :last_name, required: true, class: 'form-control' %></div>
8
- </fieldset>
9
- <fieldset>
10
- <div><%= form.label(t :gender) %></div>
11
- <div><%= form.select :gender, options_for_select(User.genders.map { |gender, _| [t(gender), gender] }, @user.gender), {}, required: true, class: 'form-control form-select' %></div>
12
- </fieldset>
13
- <fieldset>
14
- <div><%= form.label(t :birthdate) %></div>
15
- <div><%= form.date_field :birthdate, min: Date.new(1900), max: 3.years.ago.end_of_year, required: true, class: 'form-control' %></div>
16
- </fieldset>
1
+ <div class="row">
2
+ <fieldset class="col-lg-6">
3
+ <div><%= form.label(t :first_name) %></div>
4
+ <div><%= form.text_field :first_name, required: true, class: 'form-control' %></div>
5
+ </fieldset>
6
+ <fieldset class="col-lg-6">
7
+ <div><%= form.label(t :last_name) %></div>
8
+ <div><%= form.text_field :last_name, required: true, class: 'form-control' %></div>
9
+ </fieldset>
10
+ </div>
11
+ <div class="row">
12
+ <fieldset class="col-lg-4">
13
+ <div><%= form.label(t :birthdate) %></div>
14
+ <div><%= form.date_field :birthdate, min: Date.new(1900), max: 3.years.ago.end_of_year, required: true, class: 'form-control' %></div>
15
+ </fieldset>
16
+ <fieldset class="col-lg-4 offset-lg-2">
17
+ <div><%= form.label(t :gender) %></div>
18
+ <div><%= form.select :gender, options_for_select(User.genders.map { |gender, _| [t(gender), gender] }, @user.gender), {}, required: true, class: 'form-control form-select' %></div>
19
+ </fieldset>
20
+ </div>
17
21
  <fieldset>
18
22
  <div><%= form.label(t :email) %></div>
19
23
  <div><%= form.text_field :email, readonly: true, class: 'form-control' %></div>
@@ -1,22 +1,24 @@
1
1
  <%= form_for :user, url: user_path, :html => { id: 'mu-user-form', class: 'mu-form' }, method: :put do |f| %>
2
2
  <div class="mu-user-header">
3
3
  <h1><%= t(:edit_profile) %></h1>
4
- <div class="mu-profile-actions d-none d-md-block">
5
- <%= cancel_edit_profile_button %>
6
- <%= save_edit_profile_button f %>
7
- </div>
8
4
  </div>
9
5
  <div class="row mu-tab-body">
10
- <div class="col-md-6 col-lg-4 mu-user-avatar-container">
6
+ <div class="col-md-6 col-lg-4 mu-user-avatar-container mb-3">
11
7
  <%= profile_picture_for(@user, id: 'mu-user-avatar', class: 'mu-user-avatar') %>
12
8
  <button class="fas fa-pencil-alt fa-2x btn mu-edit-avatar" type="button" data-bs-toggle="modal" data-bs-target="#mu-avatar-picker" tabindex="0"></button>
13
9
  </div>
14
- <div class="col-md-6 col-lg-8">
10
+ <div class="col-md-6 col-lg-8 mb-4">
11
+ <% unless current_user.profile_completed? %>
12
+ <div class="alert alert-info alert-dismissible fade show" role="alert">
13
+ <%= fa_icon 'info-circle', class: 'fa-lg me-2 align-middle', text: t(:please_fill_profile_data) %>
14
+ <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
15
+ </div>
16
+ <% end %>
15
17
  <%= render partial: 'profile_fields', locals: {form: f} %>
16
18
  <%= render partial: 'layouts/terms_acceptance_disclaimer', locals: {user: @user} %>
17
19
  </div>
18
20
  </div>
19
- <div class="mu-profile-actions mobile d-block d-md-none">
21
+ <div class="mu-profile-actions">
20
22
  <%= cancel_edit_profile_button %>
21
23
  <%= save_edit_profile_button f %>
22
24
  </div>
@@ -41,27 +41,27 @@ module Mumuki::Laboratory::Controllers::DynamicErrors
41
41
  def forbidden
42
42
  message = "The operation on organization #{Organization.current} was forbidden to user #{current_user.uid} with permissions #{current_user.permissions}"
43
43
  Rails.logger.info message
44
- render_error 'forbidden', 403, locals: { explanation: :forbidden_explanation }, error_message: message
44
+ render_error 'forbidden', 403, locals: { error_code: :forbidden, explanation: :forbidden_explanation }, error_message: message
45
45
  end
46
46
 
47
47
  def disabled
48
- render_error 'forbidden', 403, locals: { explanation: :disabled_explanation }
48
+ render_error 'forbidden', 403, locals: { error_code: :disabled, explanation: :disabled_explanation }
49
49
  end
50
50
 
51
51
  def blocked_forum
52
- render_error 'forbidden', 403, locals: { explanation: :blocked_forum_explanation }
52
+ render_error 'forbidden', 403, locals: { error_code: :blocked_forum, explanation: :blocked_forum_explanation }
53
53
  end
54
54
 
55
55
  def gone
56
- render_error 'gone', 410, locals: { explanation: :gone_explanation }
56
+ render_error 'gone', 410, locals: { error_code: :gone, explanation: :gone_explanation }
57
57
  end
58
58
 
59
59
  def unprepared_organization
60
- render_error 'forbidden', 403, locals: { explanation: :unprepared_organization_explanation }
60
+ render_error 'forbidden', 403, locals: { error_code: :unprepared_organization, explanation: :unprepared_organization_explanation }
61
61
  end
62
62
 
63
63
  def disabled_organization
64
- render_error 'gone', 410, locals: { explanation: :disabled_organization_explanation }
64
+ render_error 'gone', 410, locals: { error_code: :disabled_organization, explanation: :disabled_organization_explanation }
65
65
  end
66
66
 
67
67
  def render_error(template, status, options={})
@@ -6,7 +6,7 @@ module Mumuki::Laboratory::Controllers::ValidateAccessMode
6
6
  end
7
7
 
8
8
  def validate_accessible!
9
- current_access_mode.validate_content_here! subject_container
9
+ current_access_mode.validate_content_here! subject_container unless contentless_subject?
10
10
  super
11
11
  end
12
12
 
@@ -14,6 +14,10 @@ module Mumuki::Laboratory::Controllers::ValidateAccessMode
14
14
  subject
15
15
  end
16
16
 
17
+ def contentless_subject?
18
+ false
19
+ end
20
+
17
21
  def accessible_subject
18
22
  nil
19
23
  end
@@ -291,7 +291,7 @@ es-CL:
291
291
  one: '1 abierta'
292
292
  other: '%{count} abiertas'
293
293
  organizations: Organizaciones
294
- organization_read_only_legend: 'Estás en modo lectura. Solo podrás acceder a los ejercicios que realizaste previamente y no podrás enviar nuevas soluciones'
294
+ organization_read_only_legend: 'Estás en Modo Lectura: como el curso ya finalizó no podrás enviar soluciones'
295
295
  other: Otro
296
296
  out_of_attempts: Te quedaste sin intentos. ¡Sigue al próximo ejercicio!
297
297
  output: Salida
@@ -300,7 +300,7 @@ es:
300
300
  one: '1 abierta'
301
301
  other: '%{count} abiertas'
302
302
  organizations: Organizaciones
303
- organization_read_only_legend: 'Estás en modo lectura. Solo podrás acceder a los ejercicios que realizaste previamente y no podrás enviar nuevas soluciones'
303
+ organization_read_only_legend: 'Estás en Modo Lectura: como el curso ya finalizó no podrás enviar soluciones'
304
304
  other: Otro
305
305
  out_of_attempts: Te quedaste sin intentos. ¡Seguí al proximo ejercicio!
306
306
  output: Salida
@@ -1,5 +1,5 @@
1
1
  module Mumuki
2
2
  module Laboratory
3
- VERSION = '9.18.1'
3
+ VERSION = '9.21.0'
4
4
  end
5
5
  end
@@ -10,7 +10,7 @@
10
10
  #
11
11
  # It's strongly recommended that you check this file into your version control system.
12
12
 
13
- ActiveRecord::Schema.define(version: 20210929223144) do
13
+ ActiveRecord::Schema.define(version: 20211020224011) do
14
14
 
15
15
  # These are extensions that must be enabled in order to support this database
16
16
  enable_extension "plpgsql"
@@ -387,6 +387,7 @@ ActiveRecord::Schema.define(version: 20210929223144) do
387
387
  t.datetime "deleted_at"
388
388
  t.bigint "deleted_by_id"
389
389
  t.bigint "assignment_id"
390
+ t.boolean "from_moderator"
390
391
  t.index ["approved_by_id"], name: "index_messages_on_approved_by_id"
391
392
  t.index ["assignment_id"], name: "index_messages_on_assignment_id"
392
393
  t.index ["deleted_by_id"], name: "index_messages_on_deleted_by_id"
@@ -2,19 +2,28 @@ require 'spec_helper'
2
2
 
3
3
  feature 'Guides Flow', organization_workspace: :test do
4
4
  let(:haskell) { create(:haskell) }
5
- let!(:exercises) { [
6
- create(:exercise, name: 'Foo', guide: guide, number: 1, description: 'Description of foo'),
7
- create(:exercise, name: 'Bar', guide: guide, number: 2),
8
- create(:exercise, name: 'Baz', guide: guide, number: 4)
9
- ] }
10
- let(:guide) { create(:guide, name: 'awesomeGuide', description: 'An awesome guide', language: haskell, slug: 'foo/bar', authors: authors) }
5
+ let(:exercises) do
6
+ [
7
+ create(:exercise, name: 'Foo', number: 1, description: 'Description of foo'),
8
+ create(:exercise, name: 'Bar', number: 2),
9
+ create(:exercise, name: 'Baz', number: 4)
10
+ ]
11
+ end
12
+ let(:guide) do
13
+ create(:guide,
14
+ name: 'awesomeGuide',
15
+ description: 'An awesome guide',
16
+ language: haskell,
17
+ slug: 'foo/bar',
18
+ authors: authors,
19
+ exercises: exercises)
20
+ end
11
21
  let(:authors) { nil }
12
22
  let(:guide_not_in_path) { create(:guide) }
13
23
 
14
24
  let!(:lesson) { create(:lesson, guide: guide) }
15
25
  let!(:chapter) { create(:chapter, name: 'C1', lessons: [lesson]) }
16
26
 
17
-
18
27
  let!(:complement) { create(:complement, name: 'a complement', exercises: [
19
28
  create(:exercise, name: 'complementary exercise 1'),
20
29
  create(:exercise, name: 'complementary exercise 2')
@@ -48,6 +57,19 @@ feature 'Guides Flow', organization_workspace: :test do
48
57
  end
49
58
 
50
59
  context 'existent guide' do
60
+
61
+ context 'no exercises' do
62
+ let(:exercises) { []}
63
+
64
+ scenario 'visit lesson by id' do
65
+ visit "/guides/#{lesson.guide.id}"
66
+
67
+ expect(page).to have_text('awesomeGuide')
68
+ expect(page).to have_text('An awesome guide')
69
+ expect(page).to_not have_text('Exercises')
70
+ end
71
+ end
72
+
51
73
  context 'no authors' do
52
74
  let(:authors) { '' }
53
75
 
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  feature 'Immersive redirection Flow', organization_workspace: :test, subdomain_redirection_without_port: true do
4
4
  def create_guide(name)
5
- create(:guide, name: name)
5
+ create(:guide, name: name, exercises: [create(:exercise)])
6
6
  end
7
7
 
8
8
  def create_immersive_organization(name, guides)
@@ -6,7 +6,14 @@ feature 'not found public on app' do
6
6
  let!(:some_orga) { create(:public_organization, name: 'someorga', profile: profile) }
7
7
 
8
8
  let(:profile) { Mumuki::Domain::Organization::Profile.parse json }
9
- let(:json) { { contact_email: 'some@email.com', locale: 'en', time_zone: 'Brasilia', errors_explanations: { 404 => 'Some explanation'} } }
9
+ let(:json) do
10
+ {
11
+ contact_email: 'some@email.com',
12
+ locale: 'en',
13
+ time_zone: 'Brasilia',
14
+ errors_explanations: { "not_found" => 'Some explanation'}
15
+ }
16
+ end
10
17
 
11
18
  scenario 'when route does not exist in explicit central' do
12
19
  set_subdomain_host! 'test'
@@ -14,7 +14,7 @@ feature 'Profile Flow', organization_workspace: :test do
14
14
  'submission_id' => problem.assignments.last.submission_id,
15
15
  'organization' => 'test-organization',
16
16
  'message' => {
17
- 'sender' => 'test-email@gmail.com',
17
+ 'sender' => create(:user).uid,
18
18
  'content' => 'a',
19
19
  'created_at' => '1/1/1'}} }
20
20
  let(:organization) { create(:organization, name: 'test-organization') }
@@ -14,14 +14,18 @@ feature 'Read Only Flow' do
14
14
  let(:exercise122) { create :exercise, name: 'Exercise 122' }
15
15
  let(:exercise211) { create :exercise, name: 'Exercise 211' }
16
16
  let(:exercise212) { create :exercise, name: 'Exercise 212' }
17
+ let(:exercise31) { create :exercise, name: 'Exercise 31' }
18
+ let(:exercise32) { create :exercise, name: 'Exercise 32' }
17
19
  let(:lesson11) { create :lesson, name: 'Lesson 11', exercises: [exercise111, exercise112] }
18
20
  let(:lesson12) { create :lesson, name: 'Lesson 12', exercises: [exercise121, exercise122] }
19
21
  let(:lesson21) { create :lesson, name: 'Lesson 21', exercises: [exercise211, exercise212] }
22
+ let(:complement3) { build :complement, name: 'Complement 3', exercises: [exercise31, exercise32] }
20
23
  let(:chapter1) { create :chapter, name: 'Chapter 1', lessons: [lesson11, lesson12] }
21
24
  let(:chapter2) { create :chapter, name: 'Chapter 2', lessons: [lesson21] }
22
- let(:book) { create :book, chapters: [chapter1, chapter2] }
25
+ let(:book) { create :book, chapters: [chapter1, chapter2], complements: [complement3] }
23
26
 
24
27
  let(:assignment111) { build :assignment, submitter: user, organization: organization, exercise: exercise111, status: :failed }
28
+ let(:assignment31) { build :assignment, submitter: user, organization: organization, exercise: exercise31, status: :failed }
25
29
  let(:discussion111) { build :discussion, initiator: assignment111.user, item: assignment111.exercise, organization: assignment111.organization }
26
30
 
27
31
  let(:assignment112) { build :assignment, submitter: other_user, organization: organization, exercise: exercise112, status: :failed }
@@ -32,6 +36,7 @@ feature 'Read Only Flow' do
32
36
  before { set_current_user! user }
33
37
 
34
38
  before { assignment111.save! }
39
+ before { assignment31.save! }
35
40
  before { discussion111.save! }
36
41
  before { discussion112.save! }
37
42
 
@@ -72,6 +77,12 @@ feature 'Read Only Flow' do
72
77
  expect(page).to have_text('Exercise 112')
73
78
  expect(page).to have_text('Continue this lesson!')
74
79
  end
80
+ scenario 'show complement' do
81
+ visit "/complements/#{complement3.id}"
82
+ expect(page).to have_text('Complement 3')
83
+ expect(page).to have_text('Exercise 31')
84
+ expect(page).to have_text('Exercise 32')
85
+ end
75
86
  scenario 'show exercise 111' do
76
87
  visit "/exercises/#{exercise111.id}"
77
88
  expect(page).to have_text('Exercise 111')
@@ -146,6 +157,12 @@ feature 'Read Only Flow' do
146
157
  expect(page).to have_text('Exercise 112')
147
158
  expect(page).to have_text('Continue this lesson!')
148
159
  end
160
+ scenario 'show complement' do
161
+ visit "/complements/#{complement3.id}"
162
+ expect(page).to have_text('Complement 3')
163
+ expect(page).to have_text('Exercise 31')
164
+ expect(page).to have_text('Exercise 32')
165
+ end
149
166
  scenario 'show exercise 111' do
150
167
  visit "/exercises/#{exercise111.id}"
151
168
  expect(page).to have_text('Exercise 111')
@@ -222,6 +239,12 @@ feature 'Read Only Flow' do
222
239
  expect(page).not_to have_text('Exercise 112')
223
240
  expect(page).not_to have_text('Continue this lesson!')
224
241
  end
242
+ scenario 'show complement' do
243
+ visit "/complements/#{complement3.id}"
244
+ expect(page).to have_text('Complement 3')
245
+ expect(page).to have_text('Exercise 31')
246
+ expect(page).to_not have_text('Exercise 32')
247
+ end
225
248
  scenario 'show exercise 111' do
226
249
  visit "/exercises/#{exercise111.id}"
227
250
  expect(page).to have_text('Exercise 111')
@@ -297,6 +320,10 @@ feature 'Read Only Flow' do
297
320
  visit "/lessons/#{lesson11.id}"
298
321
  expect(page).to have_text('You are not allowed to see this content')
299
322
  end
323
+ scenario 'show complement' do
324
+ visit "/complements/#{complement3.id}"
325
+ expect(page).to have_text('You are not allowed to see this content')
326
+ end
300
327
  scenario 'show exercise 111' do
301
328
  visit "/exercises/#{exercise111.id}"
302
329
  expect(page).to have_text('You are not allowed to see this content')
@@ -370,6 +397,12 @@ feature 'Read Only Flow' do
370
397
  expect(page).to have_text('Exercise 112')
371
398
  expect(page).to have_text('Continue this lesson!')
372
399
  end
400
+ scenario 'show complement' do
401
+ visit "/complements/#{complement3.id}"
402
+ expect(page).to have_text('Complement 3')
403
+ expect(page).to have_text('Exercise 31')
404
+ expect(page).to have_text('Exercise 32')
405
+ end
373
406
  scenario 'show exercise 111' do
374
407
  visit "/exercises/#{exercise111.id}"
375
408
  expect(page).to have_text('Exercise 111')
@@ -434,6 +467,10 @@ feature 'Read Only Flow' do
434
467
  visit "/lessons/#{lesson11.id}"
435
468
  expect(page).to have_text('This path hasn\'t started yet')
436
469
  end
470
+ scenario 'show complemen' do
471
+ visit "/complemens/#{complement3.id}"
472
+ expect(page).to have_text('This path hasn\'t started yet')
473
+ end
437
474
  scenario 'show exercise 111' do
438
475
  visit "/exercises/#{exercise111.id}"
439
476
  expect(page).to have_text('This path hasn\'t started yet')
@@ -470,20 +507,22 @@ feature 'Read Only Flow' do
470
507
 
471
508
  context 'and user is ex student of organization' do
472
509
  before { user.update! permissions: { ex_student: slug } }
473
- scenario 'avatar dorpdown' do
510
+ scenario 'avatar dropdown' do
474
511
  visit "/"
475
512
  find('#profileDropdown').click
476
- expect(page).not_to have_text('My account')
477
- expect(page).not_to have_text('FAQs')
478
- expect(page).not_to have_text('Classroom')
479
- expect(page).not_to have_text('Solve other\'s doubts')
513
+ expect(page).to have_text('My account')
514
+ expect(page).to have_text('FAQs')
480
515
  expect(page).not_to have_text('My doubts')
516
+ expect(page).not_to have_text('Classroom')
481
517
  expect(page).not_to have_text('Bibliotheca')
518
+ expect(page).not_to have_text('Solve other\'s doubts')
482
519
  expect(page).to have_text('Sign Out')
483
520
  end
484
521
  scenario 'show book' do
485
522
  visit "/"
486
- expect(page).to have_text('You are not allowed to see this content')
523
+ expect(page).not_to have_text('Chapter 1')
524
+ expect(page).not_to have_text('Chapter 2')
525
+ expect(page).not_to have_text('Practicing!')
487
526
  end
488
527
  scenario 'show chapter' do
489
528
  visit "/chapters/#{chapter1.id}"
@@ -493,6 +532,10 @@ feature 'Read Only Flow' do
493
532
  visit "/lessons/#{lesson11.id}"
494
533
  expect(page).to have_text('You are not allowed to see this content')
495
534
  end
535
+ scenario 'show complement' do
536
+ visit "/complements/#{complement3.id}"
537
+ expect(page).to have_text('You are not allowed to see this content')
538
+ end
496
539
  scenario 'show exercise 111' do
497
540
  visit "/exercises/#{exercise111.id}"
498
541
  expect(page).to have_text('You are not allowed to see this content')
@@ -503,27 +546,23 @@ feature 'Read Only Flow' do
503
546
  end
504
547
  scenario 'show profile' do
505
548
  visit "/user"
506
- expect(page).to have_text('You are not allowed to see this content')
549
+ expect(page).to have_text('My profile')
507
550
  end
508
551
  scenario 'show faqs' do
509
552
  visit "/faqs"
510
- expect(page).to have_text('You are not allowed to see this content')
511
- end
512
- scenario 'show discussion' do
513
- visit "/discussions"
514
- expect(page).to have_text('You are not allowed to see this content')
553
+ expect(page).to have_text('FAQs')
515
554
  end
516
555
  scenario 'show discussion in existent exercise' do
517
556
  visit "/exercises/#{exercise111.id}/discussions/#{discussion111.id}"
518
- expect(page).to have_text('You are not allowed to see this content')
557
+ expect(page).to have_text('Page was not found')
519
558
  end
520
559
  scenario 'show discussion in existent exercise' do
521
560
  visit "/exercises/#{exercise112.id}/discussions/#{discussion112.id}"
522
- expect(page).to have_text('You are not allowed to see this content')
561
+ expect(page).to have_text('Page was not found')
523
562
  end
524
563
  scenario 'new discussion' do
525
564
  visit "/exercises/#{exercise112.id}/discussions/new"
526
- expect(page).to have_text('You are not allowed to see this content')
565
+ expect(page).to have_text('Page was not found')
527
566
  end
528
567
  end
529
568
 
@@ -552,6 +591,10 @@ feature 'Read Only Flow' do
552
591
  visit "/lessons/#{lesson11.id}"
553
592
  expect(page).to have_text('You are not allowed to see this content')
554
593
  end
594
+ scenario 'show complement' do
595
+ visit "/complements/#{complement3.id}"
596
+ expect(page).to have_text('You are not allowed to see this content')
597
+ end
555
598
  scenario 'show exercise 111' do
556
599
  visit "/exercises/#{exercise111.id}"
557
600
  expect(page).to have_text('You are not allowed to see this content')
@@ -625,6 +668,12 @@ feature 'Read Only Flow' do
625
668
  expect(page).to have_text('Exercise 112')
626
669
  expect(page).to have_text('Continue this lesson!')
627
670
  end
671
+ scenario 'show complement' do
672
+ visit "/complements/#{complement3.id}"
673
+ expect(page).to have_text('Complement 3')
674
+ expect(page).to have_text('Exercise 31')
675
+ expect(page).to have_text('Exercise 32')
676
+ end
628
677
  scenario 'show exercise 111' do
629
678
  visit "/exercises/#{exercise111.id}"
630
679
  expect(page).to have_text('Exercise 111')
@@ -699,6 +748,12 @@ feature 'Read Only Flow' do
699
748
  expect(page).to have_text('Exercise 112')
700
749
  expect(page).not_to have_text('Continue this lesson!')
701
750
  end
751
+ scenario 'show complement' do
752
+ visit "/complements/#{complement3.id}"
753
+ expect(page).to have_text('Complement 3')
754
+ expect(page).to have_text('Exercise 31')
755
+ expect(page).to have_text('Exercise 32')
756
+ end
702
757
  scenario 'show exercise 111' do
703
758
  visit "/exercises/#{exercise111.id}"
704
759
  expect(page).to have_text('Exercise 111')
@@ -740,7 +795,7 @@ feature 'Read Only Flow' do
740
795
 
741
796
  context 'and user is ex student of organization' do
742
797
  before { user.update! permissions: { ex_student: slug } }
743
- scenario 'avatar dorpdown' do
798
+ scenario 'avatar dorpdownz' do
744
799
  visit "/"
745
800
  find('#profileDropdown').click
746
801
  expect(page).to have_text('My account')
@@ -765,6 +820,10 @@ feature 'Read Only Flow' do
765
820
  visit "/lessons/#{lesson11.id}"
766
821
  expect(page).to have_text('You are not allowed to see this content')
767
822
  end
823
+ scenario 'show complement' do
824
+ visit "/complements/#{complement3.id}"
825
+ expect(page).to have_text('You are not allowed to see this content')
826
+ end
768
827
  scenario 'show exercise 111' do
769
828
  visit "/exercises/#{exercise111.id}"
770
829
  expect(page).to have_text('You are not allowed to see this content')
@@ -821,6 +880,10 @@ feature 'Read Only Flow' do
821
880
  visit "/lessons/#{lesson11.id}"
822
881
  expect(page).to have_text('You are not allowed to see this content')
823
882
  end
883
+ scenario 'show complement' do
884
+ visit "/complements/#{complement3.id}"
885
+ expect(page).to have_text('You are not allowed to see this content')
886
+ end
824
887
  scenario 'show exercise 111' do
825
888
  visit "/exercises/#{exercise111.id}"
826
889
  expect(page).to have_text('You are not allowed to see this content')
@@ -891,6 +954,12 @@ feature 'Read Only Flow' do
891
954
  expect(page).to have_text('Exercise 112')
892
955
  expect(page).to have_text('Continue this lesson!')
893
956
  end
957
+ scenario 'show complement' do
958
+ visit "/complements/#{complement3.id}"
959
+ expect(page).to have_text('Complement 3')
960
+ expect(page).to have_text('Exercise 31')
961
+ expect(page).to have_text('Exercise 32')
962
+ end
894
963
  scenario 'show exercise 111' do
895
964
  visit "/exercises/#{exercise111.id}"
896
965
  expect(page).to have_text('Exercise 111')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mumuki-laboratory
3
3
  version: !ruby/object:Gem::Version
4
- version: 9.18.1
4
+ version: 9.21.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Franco Bulgarelli
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-10-15 00:00:00.000000000 Z
11
+ date: 2021-10-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -30,14 +30,14 @@ dependencies:
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 9.18.0
33
+ version: 9.21.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 9.18.0
40
+ version: 9.21.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: mumukit-bridge
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -438,7 +438,6 @@ files:
438
438
  - app/assets/javascripts/mumuki_laboratory/application/upload.js
439
439
  - app/assets/javascripts/mumuki_laboratory/application/user.js
440
440
  - app/assets/stylesheets/mumuki_laboratory/application.scss
441
- - app/assets/stylesheets/mumuki_laboratory/application/_alerts.scss
442
441
  - app/assets/stylesheets/mumuki_laboratory/application/_codemirror-themes.scss
443
442
  - app/assets/stylesheets/mumuki_laboratory/application/_errors.scss
444
443
  - app/assets/stylesheets/mumuki_laboratory/application/_fonts.scss
@@ -1,3 +0,0 @@
1
- .alert {
2
- margin-right: 15px;
3
- }