mumuki-laboratory 9.13.2 → 9.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/mumuki_laboratory/application/codemirror-builder.js +6 -2
  3. data/app/assets/javascripts/mumuki_laboratory/application/codemirror.js +3 -2
  4. data/app/assets/javascripts/mumuki_laboratory/application/console.js +7 -1
  5. data/app/assets/javascripts/mumuki_laboratory/application/discussions.js +8 -13
  6. data/app/assets/javascripts/mumuki_laboratory/application/exercise.js +20 -0
  7. data/app/assets/javascripts/mumuki_laboratory/application/i18n.js +4 -0
  8. data/app/assets/javascripts/mumuki_laboratory/application/results-renderer.js +6 -5
  9. data/app/controllers/ajax_controller.rb +5 -0
  10. data/app/controllers/application_controller.rb +2 -2
  11. data/app/controllers/exam_authorization_requests_controller.rb +1 -1
  12. data/app/controllers/exercise_solutions_controller.rb +1 -1
  13. data/app/helpers/discussions_helper.rb +4 -1
  14. data/app/helpers/editor_helper.rb +2 -2
  15. data/app/helpers/exam_registration_helper.rb +1 -1
  16. data/app/helpers/time_helper.rb +12 -0
  17. data/app/helpers/user_discussions_helper.rb +7 -7
  18. data/app/views/discussions/_description_message.html.erb +1 -1
  19. data/app/views/discussions/_message.html.erb +2 -2
  20. data/app/views/exam_authorization_requests/_pending.html.erb +12 -5
  21. data/app/views/exam_registrations/show.html.erb +11 -9
  22. data/app/views/exercises/_read_only.html.erb +27 -35
  23. data/app/views/layouts/_discussions_list.html.erb +1 -1
  24. data/app/views/layouts/_messages.html.erb +1 -1
  25. data/app/views/users/_user_form.html.erb +1 -1
  26. data/app/views/users/messages.html.erb +1 -1
  27. data/app/views/users/notifications.html.erb +1 -1
  28. data/lib/mumuki/laboratory/locales/es-CL.yml +6 -4
  29. data/lib/mumuki/laboratory/locales/es.yml +6 -4
  30. data/lib/mumuki/laboratory/version.rb +1 -1
  31. data/spec/features/not_found_private_flow_spec.rb +1 -1
  32. metadata +107 -106
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 19427492957bc679a64b679d1b50e33e38a750a032e9ca916fa8ba543eef8c03
4
- data.tar.gz: bab7ef25dbc9e0970471fca58b4d8e3a1efd592d9f34d092e6f0eeed4c8eeb5d
3
+ metadata.gz: '0973e0c9ab8333b9b2f4207d9cc40e3c88199c67fcee8f35415b802d0494a85a'
4
+ data.tar.gz: 7a7c28856ecb1751c25a053fe0db0eaf0607a8c1dbaa22c70071eeb26617d2ba
5
5
  SHA512:
6
- metadata.gz: bb031814c4d6bc24bcfe00e988923cbdaf0e9cdd6e033d37fd09542736be3a6d9489bc41b5b9cb85bafd52b461ef48a2483082e441a2e92a02370135ba77b3be
7
- data.tar.gz: eb0b4b0e606f74d5e679b77cb05088eebcbd651e733730acfee0f28fb15d5aecaa490d011d5bc4319cda746951419c5a85f9398472a9aa4cf40b5f5408dc5f3a
6
+ metadata.gz: 9db401ba5aa815447c273ab82597ee16c98cd0d13ca5f68720f3190114fa69f40f9195ac479d5f4fc234ef3cdfcc7fe7c2e11c8b6d2cd59a9be9194b596d2096
7
+ data.tar.gz: b06e42055b04fa8116ce5053e842d63a6c56e8a38c1c38d8b3ed2c23688ae0f0925f8208ece9c765d8987d0f3e6963cb8dcc8faa5ab11dc4e544c95af444d4a3
@@ -27,7 +27,11 @@
27
27
  this.$textarea = $(textarea);
28
28
  }
29
29
 
30
- setupEditor() {
30
+ setupEditor(readonly = false) {
31
+ return readonly ? this._setupReadOnlyEditor() : this._setupCommonEditor();
32
+ }
33
+
34
+ _setupCommonEditor() {
31
35
  this.editor = this.createEditor({
32
36
  lineNumbers: true,
33
37
  extraKeys: {
@@ -61,7 +65,7 @@
61
65
  return this;
62
66
  }
63
67
 
64
- setupReadOnlyEditor() {
68
+ _setupReadOnlyEditor() {
65
69
  this.editor = this.createEditor({
66
70
  readOnly: true,
67
71
  cursorBlinkRate: -1, //Hides the cursor
@@ -6,10 +6,11 @@ mumuki.page.editors = [];
6
6
  (() => {
7
7
  function createCodeMirrors() {
8
8
  return $(".editor").map(function (index, textarea) {
9
- var $textarea = $("#solution_content");
9
+ const $textarea = $("#solution_content");
10
+ const readonly = $textarea.data('readonly');
10
11
 
11
12
  return new mumuki.editor.CodeMirrorBuilder(textarea)
12
- .setupEditor()
13
+ .setupEditor(readonly)
13
14
  .setupMinLines($textarea.data('lines'))
14
15
  .setupLanguage()
15
16
  .build();
@@ -87,7 +87,7 @@
87
87
  }
88
88
  get content() {
89
89
  var firstEditor = mumuki.page.editors[0];
90
- if (firstEditor && $("#include_solution").prop("checked"))
90
+ if (firstEditor && this.includeSolution())
91
91
  return firstEditor.getValue();
92
92
  else
93
93
  return '';
@@ -142,6 +142,12 @@
142
142
  get _requestData() {
143
143
  return {content: this.content, query: this.line, cookie: this.cookie};
144
144
  }
145
+ includeSolution() {
146
+ return !this._includeSolutionCheckbox || this._includeSolutionCheckbox.checked;
147
+ }
148
+ get _includeSolutionCheckbox() {
149
+ return $("#include_solution")[0];
150
+ }
145
151
  }
146
152
 
147
153
 
@@ -19,25 +19,18 @@ mumuki.load(() => {
19
19
  .build();
20
20
  }
21
21
 
22
- function createReadOnlyEditors() {
23
- return $(".read-only-editor").map(function (index, textarea) {
24
- var $textarea = $("#solution_content");
25
-
26
- return new mumuki.editor.CodeMirrorBuilder(textarea)
27
- .setupReadOnlyEditor()
28
- .setupMinLines($textarea.data('lines'))
29
- .setupLanguage()
30
- .build();
31
- });
32
- }
33
-
34
- createReadOnlyEditors();
35
22
  let editor = createNewMessageEditor();
36
23
 
37
24
  var Forum = {
38
25
  toggleButton: function (elements) {
39
26
  elements.toggleClass('d-none');
40
27
  },
28
+ disableButton: function (elements) {
29
+ elements.attr('disabled', true);
30
+ },
31
+ reenableButton: function (elements) {
32
+ elements.attr('disabled', false);
33
+ },
41
34
  token: new mumuki.CsrfToken(),
42
35
  tokenRequest: function (data) {
43
36
  return $.ajax(Forum.token.newRequest(data));
@@ -56,6 +49,7 @@ mumuki.load(() => {
56
49
  Forum.discussionPostAndToggle(url, $upvoteButtons);
57
50
  },
58
51
  discussionResponsible: function (url) {
52
+ Forum.disableButton($responsibleButton);
59
53
  Forum.discussionPostToggleAndRenderToast(url, $responsibleButton);
60
54
  $('.responsible-moderator-badge').toggleClass('d-none');
61
55
  },
@@ -66,6 +60,7 @@ mumuki.load(() => {
66
60
  Forum.discussionPost(url)
67
61
  .done(function (response) {
68
62
  Forum.toggleButton(elem);
63
+ Forum.reenableButton(elem);
69
64
  mumuki.toast.addToast(response);
70
65
  })
71
66
  .fail(function (response) {
@@ -1,3 +1,23 @@
1
+ (() => {
2
+ function solutionChangedSinceLastSubmission() {
3
+ return mumuki.exercise.id &&
4
+ mumuki.SubmissionsStore.getLastSubmissionAndResult(mumuki.exercise.id) &&
5
+ !mumuki.SubmissionsStore.getSubmissionResultFor(mumuki.exercise.id, mumuki.editors.getSubmission());
6
+ }
7
+
8
+ window.addEventListener("beforeunload", (event) => {
9
+ if (solutionChangedSinceLastSubmission()) {
10
+ event.returnValue = 'unsaved_progress';
11
+ } else {
12
+ delete event['returnValue'];
13
+ }
14
+ });
15
+
16
+ window.addEventListener("turbolinks:before-visit", (event) => {
17
+ if (solutionChangedSinceLastSubmission() && !confirm(mumuki.I18n.t('unsaved_progress'))) event.preventDefault();
18
+ });
19
+ })();
20
+
1
21
  /**
2
22
  * @typedef {"input_right" | "input_bottom" | "input_primary" | "input_kindergarten"} Layout
3
23
  * @typedef {{id: number, layout: Layout, settings: any}} Exercise
@@ -9,6 +9,7 @@ mumuki.I18n = (() => {
9
9
  passed_with_warnings: () => "Tu solución funcionó, pero hay cosas que mejorar",
10
10
  pending: () => "Pendiente",
11
11
  skipped: () => "Venís aprendiendo muy bien, por lo que aprobaste este ejercicio",
12
+ unsaved_progress: () => "Tu solución tiene cambios sin guardar, ¿Querés salir de todos modos?",
12
13
  },
13
14
  'es-CL': {
14
15
  aborted: () => "Ups, no pudimos evaluar tu solución",
@@ -18,6 +19,7 @@ mumuki.I18n = (() => {
18
19
  passed_with_warnings: () => "Tu solución funcionó, pero hay cosas que mejorar",
19
20
  pending: () => "Pendiente",
20
21
  skipped: () => "Vienes aprendiendo muy bien, por lo que aprobaste este ejercicio",
22
+ unsaved_progress: () => "Tu solución tiene cambios sin guardar, ¿Quieres salir de todos modos?",
21
23
  },
22
24
  'en': {
23
25
  aborted: () => "Oops, we couldn't evaluate your solution",
@@ -27,6 +29,7 @@ mumuki.I18n = (() => {
27
29
  passed_with_warnings: () => "It worked, but you can do better",
28
30
  pending: () => "Pending",
29
31
  skipped: () => "You are doing very well, so you've passed this exercise",
32
+ unsaved_progress: () => "Your solution has unsaved changes, leave anyways?",
30
33
  },
31
34
  'pt': {
32
35
  aborted: () => "Opa, não pudemos avaliar sua solução",
@@ -36,6 +39,7 @@ mumuki.I18n = (() => {
36
39
  passed_with_warnings: () => "Sua solução funcionou, mas há coisas para melhorar",
37
40
  pending: () => "Pendente",
38
41
  skipped: () => "Você está aprendendo muito bem e passou neste exercício",
42
+ unsaved_progress: () => "Sua solução tem alterações não salvas. Deseja sair mesmo assim?",
39
43
  }
40
44
  }
41
45
 
@@ -27,11 +27,12 @@ mumuki.renderers.results = (() => {
27
27
  */
28
28
  function classForStatus(status) {
29
29
  switch (status) {
30
- case "errored": return "broken";
31
- case "failed": return "danger";
32
- case "passed_with_warnings": return "warning";
33
- case "passed": return "success";
34
- case "pending": return "muted";
30
+ case "errored": return "broken";
31
+ case "failed": return "danger";
32
+ case "manual_evaluation_pending": return "info";
33
+ case "passed_with_warnings": return "warning";
34
+ case "passed": return "success";
35
+ case "pending": return "muted";
35
36
  }
36
37
  }
37
38
 
@@ -1,9 +1,14 @@
1
1
  class AjaxController < ApplicationController
2
2
  before_action :authenticate!
3
+ before_action :validate_organization_enabled!, on: :create
3
4
 
4
5
  private
5
6
 
6
7
  def authenticate!
7
8
  head 403 unless current_user?
8
9
  end
10
+
11
+ def validate_organization_enabled!
12
+ Organization.current.validate_enabled! unless current_user&.teacher_here?
13
+ end
9
14
  end
@@ -19,11 +19,11 @@ class ApplicationController < ActionController::Base
19
19
  before_action :set_time_zone!
20
20
 
21
21
  before_action :ensure_user_enabled!, if: :current_user?
22
-
23
22
  before_action :redirect_to_proper_context!, if: :immersive_context_wrong?
24
- before_action :validate_active_organization!
25
23
 
26
24
  before_action :authorize_if_private!
25
+ before_action :validate_active_organization!
26
+
27
27
  before_action :validate_user_profile!, if: :current_user?
28
28
  before_action :validate_accepted_role_terms!, if: :current_user?
29
29
  before_action :ensure_restore_progress!, if: :current_user?
@@ -31,6 +31,6 @@ class ExamAuthorizationRequestsController < ApplicationController
31
31
 
32
32
  def verify_registration_opened!
33
33
  exam_registration = ExamRegistration.find(authorization_request_params[:exam_registration_id])
34
- raise Mumuki::Domain::GoneError if exam_registration.end_time.past?
34
+ raise Mumuki::Domain::GoneError if exam_registration.ended?
35
35
  end
36
36
  end
@@ -8,7 +8,7 @@ class ExerciseSolutionsController < AjaxController
8
8
 
9
9
  def create
10
10
  assignment = @exercise.try_submit_solution!(current_user, solution_params)
11
- render_results_json assignment, status: assignment.status
11
+ render_results_json assignment, status: assignment.visible_status
12
12
  end
13
13
 
14
14
  private
@@ -184,7 +184,10 @@ module DiscussionsHelper
184
184
  end
185
185
 
186
186
  def discussion_info(discussion)
187
- "#{t(:time_since, time: time_ago_in_words(discussion.created_at))} · #{t(:reply_count, count: discussion.visible_messages.size)}"
187
+ <<~HTML.html_safe
188
+ <span>#{friendly_time(discussion.created_at, :time_since)}</span>
189
+ <span> · #{t(:reply_count, count: discussion.visible_messages.size)}</span>
190
+ HTML
188
191
  end
189
192
 
190
193
  def discussion_filter_params_without_page
@@ -7,8 +7,8 @@ module EditorHelper
7
7
  end
8
8
 
9
9
  def read_only_editor(content, language, options = {})
10
- editor_options = editor_defaults(language, options, 'read-only-editor')
11
- text_area_tag :solution_content, content, editor_options
10
+ editor_options = editor_defaults(language, options.deep_merge(data: { readonly: true }), 'editor')
11
+ text_area_tag 'solution[content]', content, editor_options
12
12
  end
13
13
 
14
14
  def spell_checked_editor(name, options = {})
@@ -1,6 +1,6 @@
1
1
  module ExamRegistrationHelper
2
2
  def exam_registration_view
3
- if @registration.end_time.past?
3
+ if @registration.ended?
4
4
  { icon: :times_circle, class: :danger, t: :exam_registration_finished_html }
5
5
  else
6
6
  { icon: :info_circle, class: :info, t: :exam_registration_explanation_html }
@@ -0,0 +1,12 @@
1
+ module TimeHelper
2
+ def friendly_time(time, t_key = nil)
3
+ friendly_time = time_ago_in_words time
4
+ friendly_time_t = t_key ? t(t_key, time: friendly_time) : friendly_time
5
+
6
+ <<~HTML.html_safe
7
+ <time title="#{time}">
8
+ #{friendly_time_t}
9
+ </time>
10
+ HTML
11
+ end
12
+ end
@@ -1,6 +1,6 @@
1
1
  module UserDiscussionsHelper
2
2
  def user_discussions_table_title(discussion, user, last_read)
3
- %Q{
3
+ <<~HTML.html_safe
4
4
  <tr></tr>
5
5
  <thead>
6
6
  <tr>
@@ -9,30 +9,30 @@ module UserDiscussionsHelper
9
9
  </td>
10
10
  </tr>
11
11
  </thead>
12
- }.html_safe
12
+ HTML
13
13
  end
14
14
 
15
15
  def user_discussions_table_header
16
- %Q{
16
+ <<~HTML.html_safe
17
17
  <tr class="fw-bold">
18
18
  <td></td>
19
19
  <td>#{t(:exercise)}</td>
20
20
  <td>#{t(:discussion_created_by)}</td>
21
21
  <td>#{t(:last_message)}</td>
22
22
  </tr>
23
- }.html_safe
23
+ HTML
24
24
  end
25
25
 
26
26
  def user_discussions_table_item(discussion, user)
27
- %Q{
27
+ <<~HTML.html_safe
28
28
  <tr>
29
29
  <td class="text-center">
30
30
  #{icon_for_read(discussion.read_by?(user))}
31
31
  </td>
32
32
  <td>#{link_to discussion.item.name, item_discussion_path(discussion)}</td>
33
33
  <td>#{discussion_user_name discussion.initiator}</td>
34
- <td>#{t(:time_since, time: time_ago_in_words(discussion.last_message_date))}</td>
34
+ <td>#{friendly_time(discussion.last_message_date, :time_since)}</td>
35
35
  </tr>
36
- }.html_safe
36
+ HTML
37
37
  end
38
38
  end
@@ -4,7 +4,7 @@
4
4
  <div class="discussion-message-bubble-title">
5
5
  <%= linked_discussion_user_name(discussion.initiator) %>
6
6
  <span class="message-date">
7
- <%= t(:time_since, time: time_ago_in_words(discussion.created_at)) %>
7
+ <%= friendly_time(discussion.created_at, :time_since) %>
8
8
  </span>
9
9
  </div>
10
10
  </div>
@@ -7,7 +7,7 @@
7
7
  <span class="moderator-badge"><%= t(:moderation) %></span>
8
8
  <% end %>
9
9
  <span class="message-date">
10
- <%= t(:time_since, time: time_ago_in_words(message.created_at)) %>
10
+ <%= friendly_time(message.created_at, :time_since) %>
11
11
  </span>
12
12
  <span class="actions">
13
13
  <% if message.authorized?(current_user) && !message.deleted? %>
@@ -47,7 +47,7 @@
47
47
  <% if current_user&.moderator_here? %>
48
48
  <hr>
49
49
  <%= t :deleted_by, deleter: message.deleted_by.name %>
50
- <%= t(:time_since, time: time_ago_in_words(message.deleted_at)) %>.
50
+ <%= friendly_time(message.deleted_at, :time_since) %>
51
51
  <a href='<%= "#deletedMessage#{message.id}" %>' data-bs-toggle="collapse">
52
52
  <%= t :show_message %>
53
53
  </a>
@@ -1,5 +1,12 @@
1
- <%= t :exam_authorization_pending_explanation_html,
2
- date: l(authorization_request.exam.start_time, format: :long),
3
- end_time: l(authorization_request.exam_registration.end_time, format: :long),
4
- location: Organization.current.time_zone,
5
- edit_path: url_for(authorization_request.exam_registration) %>
1
+ <% if authorization_request.exam_registration.ended? %>
2
+ <%= t :exam_authorization_pending_confirmation_soon_html %>
3
+ <% elsif authorization_request.exam_registration.multiple_options? %>
4
+ <%= t :exam_authorization_pending_change_date_html,
5
+ end_time: l(authorization_request.exam_registration.end_time, format: :long),
6
+ location: Organization.current.time_zone,
7
+ edit_path: url_for(authorization_request.exam_registration) %>
8
+ <% else %>
9
+ <%= t :exam_authorization_pending_done_html,
10
+ end_time: l(authorization_request.exam_registration.end_time, format: :long),
11
+ location: Organization.current.time_zone %>
12
+ <% end %>
@@ -20,17 +20,19 @@
20
20
  <%= t exam_registration_view[:t], date: l(@registration.end_time, format: :long), location: Organization.current.time_zone %>
21
21
  </p>
22
22
  </div>
23
- <% unless @registration.end_time.past? %>
23
+ <% unless @registration.ended? %>
24
24
  <%= form_for @authorization_request, html: {class: 'mu-form'} do |f| %>
25
25
  <%= f.hidden_field :exam_registration_id, value: @registration.id %>
26
- <%= f.label :exam_id, t(:exam_registration_choose_exam) %>
27
- <% @registration.exams.each do |exam| %>
28
- <div class="form-check">
29
- <%= f.radio_button(:exam_id, exam.id, id: exam.id, required: true, class: 'form-check-input mu-read-only-input',
30
- checked: @authorization_request.exam_id == exam.id) %>
31
- <%= label_tag exam.id, "#{l(exam.start_time, format: :long)} #{current_time_zone_html}".html_safe, class: 'form-check-label' %>
32
- </div>
33
- <% end %>
26
+ <div class="mb-4">
27
+ <%= f.label :exam_id, t(:exam_registration_choose_exam) %>
28
+ <% @registration.exams.each do |exam| %>
29
+ <div class="form-check">
30
+ <%= f.radio_button(:exam_id, exam.id, id: exam.id, required: true, class: 'form-check-input mu-read-only-input',
31
+ checked: @authorization_request.exam_id == exam.id) %>
32
+ <%= label_tag exam.id, "#{l(exam.start_time, format: :long)} #{current_time_zone_html}".html_safe, class: 'form-check-label' %>
33
+ </div>
34
+ <% end %>
35
+ </div>
34
36
  <button class="btn btn-complementary"> <%= t :save %> </button>
35
37
  <% end %>
36
38
  <% end %>
@@ -47,22 +47,18 @@
47
47
 
48
48
  <% if should_render_read_only_exercise_tabs?(@discussion) %>
49
49
  <ul class="nav nav-tabs discussion-tabs" role="tablist">
50
-
51
- <% if @discussion.has_submission? %>
52
- <li role="presentation">
53
- <a class="editor-tab nav-link active" data-bs-target="#solution" aria-controls="solution" role="tab" data-bs-toggle="tab">
54
- <%= t :solution %>
55
- </a>
56
- </li>
57
- <li role="presentation">
58
- <a class="editor-tab nav-link" data-bs-target="#results" aria-controls="results" role="tab" data-bs-toggle="tab">
59
- <%= t :results %>
60
- </a>
61
- </li>
62
- <% end %>
63
-
64
50
  <li role="presentation">
65
- <a class="editor-tab nav-link <%= "active" unless @discussion.has_submission? %>" data-bs-target="#content" aria-controls="content" role="tab" data-bs-toggle="tab">
51
+ <a class="editor-tab nav-link active" data-bs-target="#solution" aria-controls="solution" role="tab" data-bs-toggle="tab">
52
+ <%= t :solution %>
53
+ </a>
54
+ </li>
55
+ <li role="presentation">
56
+ <a class="editor-tab nav-link" data-bs-target="#results" aria-controls="results" role="tab" data-bs-toggle="tab">
57
+ <%= t :results %>
58
+ </a>
59
+ </li>
60
+ <li role="presentation">
61
+ <a class="editor-tab nav-link" data-bs-target="#content" aria-controls="content" role="tab" data-bs-toggle="tab">
66
62
  <%= t :description %>
67
63
  </a>
68
64
  </li>
@@ -77,32 +73,28 @@
77
73
  </ul>
78
74
 
79
75
  <div class="tab-content">
80
- <% if @discussion.has_submission? %>
81
- <div role="tabpanel" class="tab-pane active" id="solution">
82
- <div class="mu-tab-body">
83
- <div role="tabpanel" class="tab-pane mu-input-panel fade show active" id="editor">
84
- <div class="mu-read-only-editor">
85
- <%= render_exercise_read_only_editor exercise, @discussion.solution %>
86
- </div>
87
- </div>
76
+ <div role="tabpanel" class="tab-pane mu-input-panel fade show active" id="solution">
77
+ <div class="mu-tab-body">
78
+ <div class="mu-read-only-editor">
79
+ <%= render_exercise_read_only_editor exercise, @discussion.solution %>
88
80
  </div>
89
81
  </div>
82
+ </div>
90
83
 
91
- <div role="tabpanel" class="tab-pane" id="results">
92
- <div class="mu-tab-body">
93
- <%= render layout: 'exercise_solutions/contextualization_results_container', locals: { contextualization: @discussion } do %>
94
- <div class="row">
95
- <div class="col-md-12 submission-results">
96
- <%= render partial: 'exercise_solutions/contextualization_results_body',
97
- locals: { contextualization: @discussion, guide_finished_by_solution: false } %>
98
- </div>
84
+ <div role="tabpanel" class="tab-pane fade" id="results">
85
+ <div class="mu-tab-body">
86
+ <%= render layout: 'exercise_solutions/contextualization_results_container', locals: { contextualization: @discussion } do %>
87
+ <div class="row">
88
+ <div class="col-md-12 submission-results">
89
+ <%= render partial: 'exercise_solutions/contextualization_results_body',
90
+ locals: { contextualization: @discussion, guide_finished_by_solution: false } %>
99
91
  </div>
100
- <% end %>
101
- </div>
92
+ </div>
93
+ <% end %>
102
94
  </div>
103
- <% end %>
95
+ </div>
104
96
 
105
- <div role="tabpanel" class="tab-pane <%= 'active' unless @discussion.has_submission? %>" id="content">
97
+ <div role="tabpanel" class="tab-pane fade" id="content">
106
98
  <div class="mu-tab-body">
107
99
  <div class="exercise-assignment">
108
100
  <%= render partial: 'exercises/exercise_assignment', locals: { exercise: exercise } %>
@@ -26,7 +26,7 @@
26
26
  </span>
27
27
  <span class="discussion-initiator">
28
28
  <strong><%= discussion_user_name discussion.initiator %></strong>
29
- <%= t(:asked_time_since, time: time_ago_in_words(discussion.created_at)) %>
29
+ <%= friendly_time(discussion.created_at, :asked_time_since) %>
30
30
  <span class="discussion-status-icon">
31
31
  <%= discussion_status_fa_icon(discussion) %>
32
32
  </span>
@@ -6,7 +6,7 @@
6
6
  <div class="message">
7
7
  <p> <%= message.content_html %></p>
8
8
  <div class="sender"><%= message.sender unless message.sender == current_user_uid %></div>
9
- <time><%= time_ago_in_words message.created_at %></time>
9
+ <%= friendly_time(message.created_at) %>
10
10
  </div>
11
11
  </li>
12
12
  <% end %>
@@ -31,7 +31,7 @@
31
31
  <span> <strong><%= t :email %>:</strong> <%= @user.email %> </span>
32
32
  </div>
33
33
  <div>
34
- <span> <strong><%= t :programming_since %>:</strong> <%= t(:time_since, time: time_ago_in_words(@user.created_at)) %> </span>
34
+ <span> <strong><%= t :programming_since %>:</strong> <%= friendly_time(@user.created_at, :time_since) %> </span>
35
35
  </div>
36
36
  </div>
37
37
  </div>
@@ -20,7 +20,7 @@
20
20
  <td><%= icon_for_read(message.read?) %></td>
21
21
  <td><%= link_to message.exercise.name, exercise_path(message.exercise.id) %></td>
22
22
  <td><%= mail_to message.sender %></td>
23
- <td><%= time_ago_in_words message.created_at %></td>
23
+ <td><%= friendly_time(message.created_at) %></td>
24
24
  </tr>
25
25
  <% end %>
26
26
  </table>
@@ -38,7 +38,7 @@
38
38
  <%= render partial: "notifications/#{notification.subject}", locals: { notification: notification } %>
39
39
  </td>
40
40
  <td class="col-md-3">
41
- <%= t(:time_since, time: time_ago_in_words(notification.created_at)) %>
41
+ <%= friendly_time(notification.created_at, :time_since) %>
42
42
  </td>
43
43
  </tr>
44
44
  <% end %>
@@ -116,13 +116,15 @@ es-CL:
116
116
  internal_server_error: ¡Ups! Algo no anduvo bien
117
117
  not_found: ¡Ups! La página no existe
118
118
  errored: ¡Ups! Tu solución no se puede ejecutar
119
- exam_authorization_pending_explanation_html: Tienes tiempo hasta el <strong>%{end_time}</strong> (hora de %{location}) para inscribirte. Pasada esa fecha, tu solicitud será evaluada y recibirás una confirmación. <br> ¡No olvides revisar las notificaciones!
119
+ exam_authorization_pending_confirmation_soon_html: El período de inscripción a este examen ha finalizado. Tu solicitud será evaluada pronto y recibirás una confirmación. <br> ¡No olvides revisar las notificaciones!
120
+ exam_authorization_pending_change_date_html: Tienes tiempo hasta el <strong>%{end_time}</strong> (hora de %{location}) para cambiar el turno haciendo click <strong><a href="%{edit_path}">aquí</a></strong>. Pasada esa fecha, tu solicitud será evaluada y recibirás una confirmación. <br> ¡No olvides revisar las notificaciones!
121
+ exam_authorization_pending_done_html: Ya te inscribiste a este examen. Luego del <strong>%{end_time}</strong> (hora de %{location}) se evaluará tu solicitud y recibirás una confirmación. <br> ¡No olvides de revisar las notificaciones!
120
122
  exam_authorization_request_approved_html: Tu solicitud fue aprobada, no olvides conectarte el <strong>%{date}</strong> (hora de %{location}). ¡Buena suerte!
121
- exam_authorization_request_created: ¡Tu inscripción al exámen se registró exitosamente!
123
+ exam_authorization_request_created: ¡Tu inscripción al examen se registró exitosamente!
122
124
  exam_authorization_request_rejected: Tu solicitud fue rechazada porque no cumpliste con los requisitos para rendir el examen.
123
- exam_authorization_request_saved: ¡Tu inscripción al exámen se modificó exitosamente!
125
+ exam_authorization_request_saved: ¡Tu inscripción al examen se modificó exitosamente!
124
126
  exam_authorization_request_updated: Hay novedades sobre tu inscripción a %{description}
125
- exam_registration_choose_exam: Seleccioná día y horario en el que te gustaría rendir el exámen
127
+ exam_registration_choose_exam: Seleccioná día y horario en el que te gustaría rendir el examen
126
128
  exam_registration_explanation_html: Tienes tiempo hasta el <strong>%{date}</strong> (hora de %{location}) para inscribirte. Pasada esa fecha, tu solicitud será evaluada y recibirás una confirmación. <br> ¡No olvides revisar las notificaciones!
127
129
  exam_registration_finished_html: La inscripción a este examen finalizó el <strong>%{date}</strong> (hora de %{location})
128
130
  exam_registration_open: ¡Ya puedes inscribirte a %{description}!
@@ -123,13 +123,15 @@ es:
123
123
  internal_server_error: ¡Ups! Algo no anduvo bien
124
124
  not_found: ¡Ups! La página no existe
125
125
  errored: ¡Ups! Tu solución no se puede ejecutar
126
- exam_authorization_pending_explanation_html: Tenés tiempo hasta el <strong>%{end_time}</strong> (hora de %{location}) para cambiar el turno haciendo click <strong><a href="%{edit_path}">acá</a></strong>. Pasada esa fecha, tu solicitud será evaluada y recibirás una confirmación. <br> ¡No te olvides de revisar las notificaciones!
126
+ exam_authorization_pending_confirmation_soon_html: El período de inscripción a este examen ha finalizado. Tu solicitud será evaluada pronto y recibirás una confirmación. <br> ¡No te olvides de revisar las notificaciones!
127
+ exam_authorization_pending_change_date_html: Tenés tiempo hasta el <strong>%{end_time}</strong> (hora de %{location}) para cambiar el turno haciendo click <strong><a href="%{edit_path}">acá</a></strong>. Pasada esa fecha, tu solicitud será evaluada y recibirás una confirmación. <br> ¡No te olvides de revisar las notificaciones!
128
+ exam_authorization_pending_done_html: Ya te inscribiste a este examen. Luego del <strong>%{end_time}</strong> (hora de %{location}) se evaluará tu solicitud y recibirás una confirmación. <br> ¡No te olvides de revisar las notificaciones!
127
129
  exam_authorization_request_approved_html: Tu solicitud fue aprobada, no olvides conectarte el <strong>%{date}</strong> (hora de %{location}). ¡Buena suerte!
128
- exam_authorization_request_created: ¡Tu inscripción al exámen se registró exitosamente!
130
+ exam_authorization_request_created: ¡Tu inscripción al examen se registró exitosamente!
129
131
  exam_authorization_request_rejected: Tu solicitud fue rechazada porque no cumpliste con los requisitos para rendir el examen.
130
- exam_authorization_request_saved: ¡Tu inscripción al exámen se modificó exitosamente!
132
+ exam_authorization_request_saved: ¡Tu inscripción al examen se modificó exitosamente!
131
133
  exam_authorization_request_updated: Hay novedades sobre tu inscripción a %{description}
132
- exam_registration_choose_exam: Seleccioná día y horario en el que te gustaría rendir el exámen
134
+ exam_registration_choose_exam: Seleccioná día y horario en el que te gustaría rendir el examen
133
135
  exam_registration_explanation_html: Tenés tiempo hasta el <strong>%{date}</strong> (hora de %{location}) para inscribirte. Pasada esa fecha, tu solicitud será evaluada y recibirás una confirmación. <br> ¡No te olvides de revisar las notificaciones!
134
136
  exam_registration_finished_html: La inscripción a este examen finalizó el <strong>%{date}</strong> (hora de %{location})
135
137
  exam_registration_open: ¡Ya podés inscribirte a %{description}!
@@ -1,5 +1,5 @@
1
1
  module Mumuki
2
2
  module Laboratory
3
- VERSION = '9.13.2'
3
+ VERSION = '9.16.0'
4
4
  end
5
5
  end
@@ -30,7 +30,7 @@ feature 'not found private on app', organization_workspace: :base do
30
30
  expect(page.text).to json_eq errors: [
31
31
  'The operation on organization base' +
32
32
  ' was forbidden to user foo+1@bar.com' +
33
- ' with permissions !student:central/*;teacher:;headmaster:;janitor:;admin:;owner:;ex_student:']
33
+ ' with permissions !student:central/*;teacher:;headmaster:;janitor:;admin:;owner:']
34
34
  end
35
35
 
36
36
  scenario 'api with authentication', :json_eq_error do
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.13.2
4
+ version: 9.16.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-08-26 00:00:00.000000000 Z
11
+ date: 2021-09-16 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.13.1
33
+ version: 9.16.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.13.1
40
+ version: 9.16.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: mumukit-bridge
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -567,6 +567,7 @@ files:
567
567
  - app/helpers/progress_bar_helper.rb
568
568
  - app/helpers/progress_helper.rb
569
569
  - app/helpers/runner_assets_helper.rb
570
+ - app/helpers/time_helper.rb
570
571
  - app/helpers/time_zone_helper.rb
571
572
  - app/helpers/toast_helper.rb
572
573
  - app/helpers/user_activity_helper.rb
@@ -980,135 +981,135 @@ signing_key:
980
981
  specification_version: 4
981
982
  summary: Code assement web application for the Mumuki Platform.
982
983
  test_files:
983
- - spec/capybara_helper.rb
984
- - spec/api_helper.rb
985
- - spec/features/lessons_flow_spec.rb
986
- - spec/features/login_flow_spec.rb
987
- - spec/features/exercise_flow_spec.rb
988
- - spec/features/immersive_redirection_spec.rb
989
- - spec/features/disabled_organization_flow_spec.rb
990
- - spec/features/discussion_flow_spec.rb
991
- - spec/features/menu_bar_spec.rb
992
- - spec/features/invitations_flow_spec.rb
993
- - spec/features/standard_flow_spec.rb
994
- - spec/features/not_found_public_flow_spec.rb
995
- - spec/features/home_public_flow_spec.rb
996
- - spec/features/chapters_flow_spec.rb
997
984
  - spec/features/disable_user_flow_spec.rb
998
- - spec/features/links_flow_spec.rb
985
+ - spec/features/exercise_flow_spec.rb
999
986
  - spec/features/certificate_programs_flow_spec.rb
1000
- - spec/features/exams_flow_spec.rb
1001
- - spec/features/user_activity_flow_spec.rb
987
+ - spec/features/progressive_tips_spec.rb
988
+ - spec/features/runner_assets_spec.rb
1002
989
  - spec/features/guide_reset_spec.rb
1003
- - spec/features/dynamic_exam_spec.rb
990
+ - spec/features/home_public_flow_spec.rb
991
+ - spec/features/links_flow_spec.rb
1004
992
  - spec/features/not_found_private_flow_spec.rb
993
+ - spec/features/dynamic_exam_spec.rb
994
+ - spec/features/chapters_flow_spec.rb
995
+ - spec/features/read_only_flow_spec.rb
996
+ - spec/features/discussion_flow_spec.rb
997
+ - spec/features/complements_flow_spec.rb
998
+ - spec/features/standard_flow_spec.rb
999
+ - spec/features/user_activity_flow_spec.rb
1000
+ - spec/features/invitations_flow_spec.rb
1001
+ - spec/features/home_private_flow_spec.rb
1002
+ - spec/features/exams_flow_spec.rb
1003
+ - spec/features/topic_flow_spec.rb
1004
+ - spec/features/login_flow_spec.rb
1005
+ - spec/features/not_found_public_flow_spec.rb
1006
+ - spec/features/immersive_redirection_spec.rb
1007
+ - spec/features/disabled_organization_flow_spec.rb
1008
+ - spec/features/lessons_flow_spec.rb
1005
1009
  - spec/features/guides_flow_spec.rb
1006
- - spec/features/profile_flow_spec.rb
1007
1010
  - spec/features/notifications_flow_spec.rb
1011
+ - spec/features/menu_bar_spec.rb
1012
+ - spec/features/profile_flow_spec.rb
1008
1013
  - spec/features/terms_flow_spec.rb
1009
- - spec/features/home_private_flow_spec.rb
1010
- - spec/features/read_only_flow_spec.rb
1011
- - spec/features/runner_assets_spec.rb
1012
- - spec/features/topic_flow_spec.rb
1013
- - spec/features/complements_flow_spec.rb
1014
- - spec/features/progressive_tips_spec.rb
1015
- - spec/helpers/breadcrumbs_helper_spec.rb
1016
- - spec/helpers/certificate_helper_spec.rb
1017
- - spec/helpers/avatar_helper_spec.rb
1018
- - spec/helpers/with_navigation_spec.rb
1019
- - spec/helpers/authors_helper_spec.rb
1020
- - spec/helpers/with_choices_spec.rb
1021
- - spec/helpers/icons_helper_spec.rb
1022
- - spec/helpers/email_helper_spec.rb
1023
- - spec/helpers/exercise_input_helper_spec.rb
1024
- - spec/helpers/user_activity_helper_spec.rb
1025
- - spec/helpers/test_results_rendering_spec.rb
1026
- - spec/helpers/application_helper_spec.rb
1027
- - spec/helpers/page_title_helper_spec.rb
1014
+ - spec/javascripts/results-renderers-spec.js
1015
+ - spec/javascripts/i18n-spec.js
1016
+ - spec/javascripts/gamification-spec.js
1017
+ - spec/javascripts/global-spec.js
1018
+ - spec/javascripts/elipsis-spec.js
1019
+ - spec/javascripts/kids-button-spec.js
1020
+ - spec/javascripts/sync-mode-spec.js
1021
+ - spec/javascripts/spec-helper.js
1022
+ - spec/javascripts/timer-spec.js
1023
+ - spec/javascripts/csrf-token-spec.js
1024
+ - spec/javascripts/submissions-store-spec.js
1025
+ - spec/javascripts/timeout-spec.js
1026
+ - spec/javascripts/editors-spec.js
1027
+ - spec/javascripts/speech-bubble-renderer-spec.js
1028
+ - spec/javascripts/upload-spec.js
1029
+ - spec/javascripts/exercise-spec.js
1030
+ - spec/javascripts/events-spec.js
1031
+ - spec/javascripts/bridge-spec.js
1032
+ - spec/spec_helper.rb
1033
+ - spec/teaspoon_env.rb
1028
1034
  - spec/evaluation_helper.rb
1029
- - spec/dummy/Rakefile
1030
- - spec/dummy/package.json
1031
- - spec/dummy/bin/setup
1032
- - spec/dummy/bin/bundle
1033
- - spec/dummy/bin/yarn
1034
- - spec/dummy/bin/rake
1035
- - spec/dummy/bin/rails
1036
- - spec/dummy/bin/update
1037
- - spec/dummy/public/error/timeout_2.svg
1035
+ - spec/dummy/public/medal/outline.svg
1036
+ - spec/dummy/public/error/404.svg
1037
+ - spec/dummy/public/error/500.svg
1038
1038
  - spec/dummy/public/error/timeout_3.svg
1039
1039
  - spec/dummy/public/error/timeout_1.svg
1040
- - spec/dummy/public/error/500.svg
1041
- - spec/dummy/public/error/403.svg
1040
+ - spec/dummy/public/error/timeout_2.svg
1042
1041
  - spec/dummy/public/error/401.svg
1042
+ - spec/dummy/public/error/403.svg
1043
1043
  - spec/dummy/public/error/410.svg
1044
- - spec/dummy/public/error/404.svg
1045
- - spec/dummy/public/character/magnifying_glass/loop.svg
1044
+ - spec/dummy/public/character/animations.json
1046
1045
  - spec/dummy/public/character/magnifying_glass/apparition.svg
1047
- - spec/dummy/public/character/kibi/success_l.svg
1046
+ - spec/dummy/public/character/magnifying_glass/loop.svg
1048
1047
  - spec/dummy/public/character/kibi/failure.svg
1049
- - spec/dummy/public/character/kibi/jump.svg
1048
+ - spec/dummy/public/character/kibi/context.svg
1050
1049
  - spec/dummy/public/character/kibi/passed_with_warnings.svg
1050
+ - spec/dummy/public/character/kibi/jump.svg
1051
+ - spec/dummy/public/character/kibi/success_l.svg
1051
1052
  - spec/dummy/public/character/kibi/success2_l.svg
1052
- - spec/dummy/public/character/kibi/context.svg
1053
- - spec/dummy/public/character/animations.json
1054
- - spec/dummy/public/medal/outline.svg
1055
- - spec/dummy/db/schema.rb
1056
- - spec/dummy/db/seeds.rb
1057
- - spec/dummy/config.ru
1058
- - spec/dummy/config/secrets.yml
1053
+ - spec/dummy/Rakefile
1054
+ - spec/dummy/config/application.rb
1059
1055
  - spec/dummy/config/puma.rb
1060
- - spec/dummy/config/boot.rb
1061
- - spec/dummy/config/locales/en.yml
1062
- - spec/dummy/config/environment.rb
1056
+ - spec/dummy/config/spring.rb
1057
+ - spec/dummy/config/routes.rb
1058
+ - spec/dummy/config/environments/test.rb
1059
+ - spec/dummy/config/environments/development.rb
1060
+ - spec/dummy/config/database.yml
1063
1061
  - spec/dummy/config/initializers/assets.rb
1064
1062
  - spec/dummy/config/initializers/wrap_parameters.rb
1065
- - spec/dummy/config/initializers/cookies_serializer.rb
1066
1063
  - spec/dummy/config/initializers/filter_parameter_logging.rb
1067
- - spec/dummy/config/database.yml
1068
- - spec/dummy/config/routes.rb
1069
- - spec/dummy/config/spring.rb
1064
+ - spec/dummy/config/initializers/cookies_serializer.rb
1070
1065
  - spec/dummy/config/rabbit.yml
1071
- - spec/dummy/config/environments/development.rb
1072
- - spec/dummy/config/environments/test.rb
1073
- - spec/dummy/config/application.rb
1066
+ - spec/dummy/config/locales/en.yml
1074
1067
  - spec/dummy/config/cable.yml
1075
- - spec/teaspoon_env.rb
1076
- - spec/controllers/exercise_solutions_controller_spec.rb
1068
+ - spec/dummy/config/environment.rb
1069
+ - spec/dummy/config/secrets.yml
1070
+ - spec/dummy/config/boot.rb
1071
+ - spec/dummy/package.json
1072
+ - spec/dummy/config.ru
1073
+ - spec/dummy/db/seeds.rb
1074
+ - spec/dummy/db/schema.rb
1075
+ - spec/dummy/bin/yarn
1076
+ - spec/dummy/bin/rails
1077
+ - spec/dummy/bin/rake
1078
+ - spec/dummy/bin/bundle
1079
+ - spec/dummy/bin/setup
1080
+ - spec/dummy/bin/update
1081
+ - spec/controllers/certificates_controller_spec.rb
1082
+ - spec/controllers/students_api_controller_spec.rb
1083
+ - spec/controllers/api_clients_controller.rb
1084
+ - spec/controllers/organizations_api_controller_spec.rb
1077
1085
  - spec/controllers/exam_authorization_requests_controller_spec.rb
1078
- - spec/controllers/discussions_controller_spec.rb
1079
- - spec/controllers/guide_progress_controller_spec.rb
1086
+ - spec/controllers/confirmations_controller_spec.rb
1087
+ - spec/controllers/exam_registrations_controller_spec.rb
1080
1088
  - spec/controllers/users_controller_spec.rb
1089
+ - spec/controllers/exercise_solutions_controller_spec.rb
1090
+ - spec/controllers/discussions_controller_spec.rb
1081
1091
  - spec/controllers/courses_api_controller_spec.rb
1082
- - spec/controllers/chapters_controller_spec.rb
1083
- - spec/controllers/users_api_controller_spec.rb
1084
- - spec/controllers/exam_registrations_controller_spec.rb
1085
- - spec/controllers/students_api_controller_spec.rb
1086
- - spec/controllers/confirmations_controller_spec.rb
1087
- - spec/controllers/api_clients_controller.rb
1088
- - spec/controllers/certificates_controller_spec.rb
1089
- - spec/controllers/invitations_controller_spec.rb
1090
1092
  - spec/controllers/discussions_messages_controller_spec.rb
1093
+ - spec/controllers/users_api_controller_spec.rb
1091
1094
  - spec/controllers/messages_controller_spec.rb
1092
- - spec/controllers/organizations_api_controller_spec.rb
1093
- - spec/javascripts/elipsis-spec.js
1094
- - spec/javascripts/bridge-spec.js
1095
- - spec/javascripts/timeout-spec.js
1096
- - spec/javascripts/results-renderers-spec.js
1097
- - spec/javascripts/submissions-store-spec.js
1098
- - spec/javascripts/kids-button-spec.js
1099
- - spec/javascripts/speech-bubble-renderer-spec.js
1100
- - spec/javascripts/sync-mode-spec.js
1101
- - spec/javascripts/events-spec.js
1102
- - spec/javascripts/editors-spec.js
1103
- - spec/javascripts/spec-helper.js
1104
- - spec/javascripts/gamification-spec.js
1105
- - spec/javascripts/i18n-spec.js
1106
- - spec/javascripts/upload-spec.js
1107
- - spec/javascripts/timer-spec.js
1108
- - spec/javascripts/global-spec.js
1109
- - spec/javascripts/exercise-spec.js
1110
- - spec/javascripts/csrf-token-spec.js
1095
+ - spec/controllers/invitations_controller_spec.rb
1096
+ - spec/controllers/guide_progress_controller_spec.rb
1097
+ - spec/controllers/chapters_controller_spec.rb
1111
1098
  - spec/login_helper.rb
1112
- - spec/spec_helper.rb
1113
1099
  - spec/mailers/previews/user_mailer_preview.rb
1114
1100
  - spec/mailers/user_mailer_spec.rb
1101
+ - spec/capybara_helper.rb
1102
+ - spec/helpers/icons_helper_spec.rb
1103
+ - spec/helpers/certificate_helper_spec.rb
1104
+ - spec/helpers/test_results_rendering_spec.rb
1105
+ - spec/helpers/avatar_helper_spec.rb
1106
+ - spec/helpers/exercise_input_helper_spec.rb
1107
+ - spec/helpers/with_navigation_spec.rb
1108
+ - spec/helpers/email_helper_spec.rb
1109
+ - spec/helpers/user_activity_helper_spec.rb
1110
+ - spec/helpers/with_choices_spec.rb
1111
+ - spec/helpers/breadcrumbs_helper_spec.rb
1112
+ - spec/helpers/page_title_helper_spec.rb
1113
+ - spec/helpers/application_helper_spec.rb
1114
+ - spec/helpers/authors_helper_spec.rb
1115
+ - spec/api_helper.rb