mumuki-laboratory 5.3.0 → 5.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +6 -1
  3. data/app/assets/javascripts/application/kids.js +47 -17
  4. data/app/assets/stylesheets/application/modules/_kids.scss +20 -0
  5. data/app/assets/stylesheets/application.scss +1 -1
  6. data/app/controllers/invitations_controller.rb +2 -2
  7. data/app/helpers/application_helper.rb +12 -2
  8. data/app/mailers/user_mailer.rb +12 -3
  9. data/app/models/assignment.rb +8 -0
  10. data/app/models/concerns/assistable.rb +17 -0
  11. data/app/models/concerns/with_expectations.rb +1 -5
  12. data/app/models/concerns/with_reminders.rb +53 -9
  13. data/app/models/concerns/with_slug.rb +1 -1
  14. data/app/models/concerns/with_status.rb +1 -1
  15. data/app/models/exercise.rb +2 -2
  16. data/app/models/organization.rb +8 -0
  17. data/app/models/submission/submission.rb +3 -1
  18. data/app/models/user.rb +4 -0
  19. data/app/views/book/show.html.erb +1 -1
  20. data/app/views/exercise_solutions/_expectations.html.erb +7 -0
  21. data/app/views/exercise_solutions/_results.html.erb +6 -3
  22. data/app/views/layouts/_kids.html.erb +3 -3
  23. data/app/views/layouts/application.html.erb +4 -5
  24. data/app/views/user_mailer/no_submissions_reminder.html.erb +349 -0
  25. data/app/views/user_mailer/no_submissions_reminder.text.erb +13 -0
  26. data/db/migrate/20180307150148_add_failed_submissions_count_to_assignments.rb +5 -0
  27. data/db/migrate/20180526141344_add_tips_rules_to_exercise.rb +5 -0
  28. data/lib/mumuki/laboratory/controllers/dynamic_errors.rb +1 -1
  29. data/lib/mumuki/laboratory/controllers/results_rendering.rb +7 -1
  30. data/lib/mumuki/laboratory/locales/en.yml +2 -0
  31. data/lib/mumuki/laboratory/locales/es.yml +2 -0
  32. data/lib/mumuki/laboratory/locales/narrator.en.yml +14 -0
  33. data/lib/mumuki/laboratory/locales/narrator.es.yml +14 -0
  34. data/lib/mumuki/laboratory/locales/narrator.pt.yml +14 -0
  35. data/lib/mumuki/laboratory/seed.rb +9 -29
  36. data/lib/mumuki/laboratory/status/base.rb +3 -1
  37. data/lib/mumuki/laboratory/status/failed.rb +0 -4
  38. data/lib/mumuki/laboratory/status/passed_with_warnings.rb +0 -4
  39. data/lib/mumuki/laboratory/status/unknown.rb +0 -4
  40. data/lib/mumuki/laboratory/version.rb +1 -1
  41. data/lib/mumuki/laboratory.rb +1 -0
  42. data/lib/tasks/users.rake +5 -4
  43. data/spec/dummy/db/schema.rb +3 -1
  44. data/spec/features/profile_flow_spec.rb +1 -1
  45. data/spec/features/progressive_tips_spec.rb +48 -0
  46. data/spec/helpers/application_helper_spec.rb +3 -5
  47. data/spec/helpers/email_helper_spec.rb +0 -2
  48. data/spec/helpers/with_navigation_spec.rb +0 -2
  49. data/spec/mailers/user_mailer_spec.rb +104 -10
  50. data/spec/models/event_generation_spec.rb +3 -0
  51. data/spec/models/exercise_spec.rb +0 -2
  52. data/spec/models/guide_import_spec.rb +7 -0
  53. data/spec/models/organization_spec.rb +0 -1
  54. data/spec/models/playground_spec.rb +0 -2
  55. data/spec/models/user_spec.rb +8 -0
  56. data/spec/spec_helper.rb +2 -0
  57. metadata +29 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2274f31d946435025a018f54c3a83f962a6574d675e1e060712bdc9e78d5c281
4
- data.tar.gz: 0a86aa34f9ee2422f41ac9062498f3d925740562a33e3eb5e6bc57239897a8d0
3
+ metadata.gz: f2a576c10df838214910df546c5ba3aca918fc08275df1b39be0191c419a1d28
4
+ data.tar.gz: e87e69c8d7a253d16b0694413db2618c37bf6de828bc72e9270fc371b4560631
5
5
  SHA512:
6
- metadata.gz: 4a73e4420c07b9ee520922d3f1ffcfa5425abc7e5f8fa3bf8311a95664b7b3e50f44faf3f7ab9b4730fcd37cde58332eaa0d9f2dbec7f4e485283212c307361d
7
- data.tar.gz: 68e1afc05bf48733ec13e5d1451e35beaf8c7d862197b4f7efe0dabd9ec31001c9afb6ef8c153b415f4cad00c400b8de64fbc960509b929afd189eeb07e0b920
6
+ metadata.gz: c8a8b571044afdd28d6a59957b0bd51ec6b92197429cd5c36e2221842d65cd65ef44c3e7b8727dafd66c8437fde9e774d3fc39e8e130b31a44ff92910224a472
7
+ data.tar.gz: 936bf126f56fcdc664c28ec9a52ea6d7cf426b347e15e1c31d32e9b07809454f9da5692dbb88a4a8dcd7b2b1b632b2c55a0eb19f76ed041227c9dbf186784b44
data/README.md CHANGED
@@ -56,11 +56,16 @@ cd mumuki-laboratory
56
56
  > We need to create a PostgreSQL role - AKA a user - who will be used by Laboratory to create and access the database
57
57
 
58
58
  ```bash
59
- # create db user
59
+ # create db user for linux users
60
60
  sudo -u postgres psql <<EOF
61
61
  create role mumuki with createdb login password 'mumuki';
62
62
  EOF
63
63
 
64
+ # create db user for mac users
65
+ psql postgres
66
+ #once inside postgres server
67
+ create role mumuki with createdb login password 'mumuki';
68
+
64
69
  # create schema and initial development data
65
70
  ./devinit
66
71
  ```
@@ -40,19 +40,18 @@ mumuki.load(function () {
40
40
 
41
41
  var $speechParagraphs;
42
42
  var currentParagraphIndex = 0;
43
- var $prevSpeech = $('.mu-kids-character-speech-bubble > .mu-kids-prev-speech').hide();
44
- var $nextSpeech = $('.mu-kids-character-speech-bubble > .mu-kids-next-speech');
43
+ var $prevSpeech = $('.mu-kids-character-speech-bubble-normal > .mu-kids-prev-speech').hide();
44
+ var $nextSpeech = $('.mu-kids-character-speech-bubble-normal > .mu-kids-next-speech');
45
+ var $speechTabs = $('.mu-kids-character-speech-bubble-tabs > li:not(.separator)');
46
+ var $bubble = $('.mu-kids-character-speech-bubble').children('.mu-kids-character-speech-bubble-normal');
47
+ var $texts = $bubble.children('.description, .hint');
45
48
 
46
49
  updateSpeechParagraphs();
47
-
48
50
  function updateSpeechParagraphs() {
49
- $speechParagraphs = $('.mu-kids-character-speech-bubble > p');
51
+ $speechParagraphs = $('.mu-kids-character-speech-bubble > .mu-kids-character-speech-bubble-normal > div.' + getSelectedTabName() + ' > p');
52
+ showParagraph(0);
50
53
  }
51
54
 
52
- var $speechTabs = $('.mu-kids-character-speech-bubble-tabs > li:not(.separator)');
53
- var $bubble = $('.mu-kids-character-speech-bubble').children('.mu-kids-character-speech-bubble-normal');
54
- var $texts = $bubble.children('.description, .hint');
55
-
56
55
  $speechTabs.each(function (i) {
57
56
  var $tab = $($speechTabs[i]);
58
57
  $tab.click(function () {
@@ -60,12 +59,12 @@ mumuki.load(function () {
60
59
  $tab.addClass('active');
61
60
  $texts.hide();
62
61
  $bubble.children('.' + $tab.data('target')).show();
62
+ $bubble.scroll(0);
63
+ hideCurrentParagraph();
63
64
  updateSpeechParagraphs();
64
65
  })
65
66
  });
66
67
 
67
- if ($speechParagraphs.length <= 1) $nextSpeech.hide();
68
-
69
68
  $nextSpeech.click(function () {
70
69
  hideCurrentParagraph();
71
70
  showNextParagraph();
@@ -75,22 +74,50 @@ mumuki.load(function () {
75
74
  showPrevParagraph();
76
75
  });
77
76
 
77
+ function getSelectedTabName() {
78
+ return $speechTabs.filter(".active").data('target');
79
+ }
80
+
78
81
  function hideCurrentParagraph() {
79
82
  $($speechParagraphs[currentParagraphIndex]).hide();
80
83
  }
81
84
 
82
85
  function showPrevParagraph() {
83
- $nextSpeech.show();
84
- $($speechParagraphs[--currentParagraphIndex]).show();
85
- if (currentParagraphIndex === 0) $prevSpeech.hide();
86
+ showParagraph(currentParagraphIndex - 1);
86
87
  }
87
88
 
88
89
  function showNextParagraph() {
89
- $prevSpeech.show();
90
- $($speechParagraphs[++currentParagraphIndex]).show();
91
- if ($speechParagraphs.length - 1 === currentParagraphIndex) $nextSpeech.hide();
90
+ showParagraph(currentParagraphIndex + 1);
92
91
  }
93
92
 
93
+ function showParagraph(index) {
94
+ $($speechParagraphs[index]).show();
95
+ setVisibility($prevSpeech, index !== 0);
96
+ setVisibility($nextSpeech, index !== $speechParagraphs.length - 1);
97
+
98
+ currentParagraphIndex = index;
99
+ }
100
+
101
+ function setVisibility(element, isVisible) {
102
+ if (isVisible) element.show(); else element.hide();
103
+ }
104
+
105
+ function autoScrollBubble(toBottom) {
106
+ var SCROLL_TIME = 1000;
107
+ var PAUSE_TIME = 2000;
108
+
109
+ setTimeout(function () {
110
+ $bubble.animate({scrollTop: toBottom ? $bubble.height() : 0}, {
111
+ duration: SCROLL_TIME,
112
+ complete: function () {
113
+ autoScrollBubble(!toBottom);
114
+ }
115
+ });
116
+ }, PAUSE_TIME);
117
+ }
118
+
119
+ autoScrollBubble();
120
+
94
121
  mumuki.kids = {
95
122
 
96
123
  getResultsModal: function () {
@@ -145,6 +172,9 @@ mumuki.load(function () {
145
172
  $bubble.find('.mu-kids-character-speech-bubble-normal').hide();
146
173
  $bubble.find('.mu-kids-character-speech-bubble-failed').show().html(data.title_html);
147
174
  $bubble.addClass(data.status);
175
+ if (data.status === 'passed_with_warnings') {
176
+ $bubble.find('.mu-kids-character-speech-bubble-failed').append(data.expectations_html);
177
+ }
148
178
  mumuki.kids.getOverlay().show();
149
179
  },
150
180
 
@@ -213,7 +243,7 @@ mumuki.load(function () {
213
243
  };
214
244
 
215
245
  mumuki.kids.resultAction.passed = mumuki.kids._showOnSuccessPopup;
216
- mumuki.kids.resultAction.passed_with_warnings = mumuki.kids._showOnSuccessPopup;
246
+ mumuki.kids.resultAction.passed_with_warnings = mumuki.kids._showOnCharacterBubble;
217
247
 
218
248
  mumuki.kids.resultAction.aborted = mumuki.kids._showOnFailurePopup;
219
249
 
@@ -104,6 +104,16 @@ $kids-speech-tabs-width: 40px;
104
104
  height: 100%;
105
105
  }
106
106
  .mu-kids-character-speech-bubble {
107
+
108
+ &.passed_with_warnings {
109
+ padding-top: 5px;
110
+ padding-bottom: 5px;
111
+
112
+ .results-list {
113
+ margin-top: -10px;
114
+ }
115
+ }
116
+
107
117
  ul {
108
118
  margin: 0;
109
119
  }
@@ -117,6 +127,16 @@ $kids-speech-tabs-width: 40px;
117
127
  cursor: pointer;
118
128
  }
119
129
  }
130
+ div.mu-kids-character-speech-bubble-normal {
131
+ width: 100%;
132
+ height: 100%;
133
+ overflow: hidden;
134
+ }
135
+ div.mu-kids-character-speech-bubble-failed {
136
+ width: 100%;
137
+ height: 100%;
138
+ overflow: hidden;
139
+ }
120
140
  ul.mu-kids-character-speech-bubble-tabs {
121
141
  display: flex;
122
142
  flex-direction: column;
@@ -2,7 +2,7 @@ $icon-font-path: '../assets/bootstrap/';
2
2
  $fa-font-path: '../assets';
3
3
  $da-font-path: '../assets';
4
4
 
5
- @import "mumuki-styles.scss";
5
+ @import "scss/mumuki-styles";
6
6
  @import "codemirror/codemirror.min";
7
7
  @import "codemirror/show-hint.min";
8
8
  @import "application/vendor";
@@ -7,7 +7,7 @@ class InvitationsController < ApplicationController
7
7
  end
8
8
 
9
9
  def join
10
- current_user.make_student_of! @invitation.course
10
+ current_user.accept_invitation! @invitation
11
11
  current_user.update! user_params
12
12
  current_user.notify!
13
13
  redirect_to_organization!
@@ -20,7 +20,7 @@ class InvitationsController < ApplicationController
20
20
  private
21
21
 
22
22
  def redirect_to_organization!
23
- redirect_to Mumukit::Platform.laboratory.organic_url @organization
23
+ redirect_to Mumukit::Platform.laboratory.organic_url(@organization)
24
24
  end
25
25
 
26
26
  def user_params
@@ -6,10 +6,12 @@ module ApplicationHelper
6
6
  end
7
7
 
8
8
  def page_title(subject)
9
+ name = "Mumuki#{Organization.current.title_suffix}"
10
+
9
11
  if subject && !subject.new_record?
10
- "#{subject.friendly} - Mumuki"
12
+ "#{subject.friendly} - #{name}"
11
13
  else
12
- "Mumuki - #{t :mumuki_catchphrase}"
14
+ "#{name} - #{t :mumuki_catchphrase}"
13
15
  end
14
16
  end
15
17
 
@@ -27,6 +29,14 @@ module ApplicationHelper
27
29
  end
28
30
  end
29
31
 
32
+ def assistance_box(assignment)
33
+ if assignment.tips.present?
34
+ %Q{<div class="mu-tips-box">
35
+ #{Mumukit::Assistant::Narrator.random.compose_explanation_html assignment.tips}
36
+ </div>}.html_safe
37
+ end
38
+ end
39
+
30
40
  def chapter_finished(chapter)
31
41
  t :chapter_finished_html, chapter: link_to_path_element(chapter) if chapter
32
42
  end
@@ -1,15 +1,24 @@
1
1
  class UserMailer < ApplicationMailer
2
2
 
3
3
  def we_miss_you_reminder(user, cycles)
4
+ send_reminder! user, t(:we_miss_you), "#{cycles.ordinalize}_reminder"
5
+ end
6
+
7
+ def no_submissions_reminder(user)
8
+ send_reminder! user, t(:start_using_mumuki), "no_submissions_reminder"
9
+ end
10
+
11
+ private
12
+
13
+ def send_reminder!(user, subject, template_name)
4
14
  @user = user
5
15
  @unsubscribe_code = User.unsubscription_verifier.generate(user.id)
6
16
  @organization = user.last_organization
7
17
 
8
18
  I18n.with_locale(@organization.locale) do
9
19
  mail to: user.email,
10
- subject: t(:we_miss_you),
11
- template_name: "#{cycles.ordinalize}_reminder"
20
+ subject: subject,
21
+ template_name: template_name
12
22
  end
13
23
  end
14
-
15
24
  end
@@ -140,6 +140,14 @@ class Assignment < ApplicationRecord
140
140
  }})
141
141
  end
142
142
 
143
+ def tips
144
+ @tips ||= exercise.assist_with(self)
145
+ end
146
+
147
+ def increment_attemps!
148
+ self.attemps_count += 1 unless passed?
149
+ end
150
+
143
151
  private
144
152
 
145
153
  def update_submissions_count!
@@ -0,0 +1,17 @@
1
+ module Assistable
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ serialize :assistance_rules, Array
6
+ end
7
+
8
+ def assistant
9
+ Mumukit::Assistant.parse(assistance_rules)
10
+ end
11
+
12
+ def assist_with(assignment)
13
+ # not strictly necessary, but avoid going through
14
+ # all the assistence process when there are no rules
15
+ assistance_rules.blank? ? [] : assistant.assist_with(assignment)
16
+ end
17
+ end
@@ -2,7 +2,7 @@ module WithExpectations
2
2
  extend ActiveSupport::Concern
3
3
 
4
4
  included do
5
- serialize :expectations
5
+ serialize :expectations, Array
6
6
  end
7
7
 
8
8
  def expectations_yaml
@@ -16,8 +16,4 @@ module WithExpectations
16
16
  def expectations=(expectations)
17
17
  self[:expectations] = expectations.map(&:stringify_keys)
18
18
  end
19
-
20
- def expectations
21
- self[:expectations] || []
22
- end
23
19
  end
@@ -1,28 +1,72 @@
1
1
  module WithReminders
2
+ extend ActiveSupport::Concern
2
3
 
3
4
  def build_reminder
4
- UserMailer.new.we_miss_you_reminder(self, cycles_since(last_submission_date))
5
+ mailer = UserMailer.new
6
+ last_submission_date.nil? ?
7
+ mailer.no_submissions_reminder(self) :
8
+ mailer.we_miss_you_reminder(self, cycles_since(last_submission_date))
5
9
  end
6
10
 
7
- def send_reminder!
11
+ def remind!
8
12
  build_reminder.deliver
9
- update! last_reminded: Time.now
13
+ update! last_reminded_date: Time.now
14
+ end
15
+
16
+ def should_remind?
17
+ reminder_due? && (has_no_submissions? || has_no_recent_submission?)
10
18
  end
11
19
 
20
+ # Try to send a reminder, by acquiring a database lock for update
21
+ # the aproppriate record. This object can't be updated as long as
22
+ # the reminder is being sent.
23
+ #
24
+ # This method is aimed to be sent across multiple servers or processed concurrently
25
+ # and still not send duplicate mails
26
+ def try_remind_with_lock!
27
+ # Some notes:
28
+ #
29
+ # * nowait is a postgre specific option and may not work with other databases
30
+ # * nowait will raise an exception if the lock can not be acquired
31
+ # * we are using a double check lock pattern to reduce lock acquisition
32
+ with_lock('for update nowait') do
33
+ reload
34
+ remind! if should_remind?
35
+ end if should_remind?
36
+ rescue
37
+ nil
38
+ end
39
+
40
+ private
41
+
12
42
  def cycles_since(time)
13
- ((Date.current - time.to_date) / Rails.configuration.reminder_frequency).to_i
43
+ ((Date.current - time.to_date) / self.class.reminder_frequency).to_i
14
44
  end
15
45
 
16
- def remider_due?
46
+ def reminder_due?
17
47
  last_reminded_date.nil? || cycles_since(last_reminded_date) >= 1
18
48
  end
19
49
 
20
- def should_send_reminder?
21
- remider_due? && cycles_since(last_submission_date).between?(1, 3)
50
+ def can_still_remind?(date)
51
+ cycles_since(date).between?(1, 3)
22
52
  end
23
53
 
24
- def remind!
25
- send_reminder! if should_send_reminder?
54
+ def has_no_submissions?
55
+ last_submission_date.nil? && can_still_remind?(created_at)
56
+ end
57
+
58
+ def has_no_recent_submission?
59
+ !last_submission_date.nil? && can_still_remind?(last_submission_date)
26
60
  end
27
61
 
62
+ module ClassMethods
63
+ def remindable
64
+ where('accepts_reminders and (last_submission_date < ? or last_submission_date is null)', reminder_frequency.days.ago)
65
+ end
66
+
67
+ # The frequency of reminders, expressed in days
68
+ def reminder_frequency
69
+ Rails.configuration.reminder_frequency
70
+ end
71
+ end
28
72
  end
@@ -8,7 +8,7 @@ module WithSlug
8
8
  end
9
9
 
10
10
  def import!
11
- import_from_json! Mumukit::Bridge::Bibliotheca.new(Mumukit::Platform.bibliotheca_api.url).send(self.class.name.underscore, slug)
11
+ import_from_json! Mumukit::Platform.bibliotheca_bridge.send(self.class.name.underscore, slug)
12
12
  end
13
13
 
14
14
  def slug_parts
@@ -11,7 +11,7 @@ module WithStatus
11
11
  end
12
12
 
13
13
  def aborted?
14
- status == :aborted
14
+ status.aborted?
15
15
  end
16
16
 
17
17
  def run_update!
@@ -4,7 +4,8 @@ class Exercise < ApplicationRecord
4
4
  include WithNumber,
5
5
  WithAssignments,
6
6
  FriendlyName,
7
- WithLanguage
7
+ WithLanguage,
8
+ Assistable
8
9
 
9
10
  include Submittable,
10
11
  Questionable
@@ -13,7 +14,6 @@ class Exercise < ApplicationRecord
13
14
  ParentNavigation
14
15
 
15
16
  belongs_to :guide
16
-
17
17
  defaults { self.submissions_count = 0 }
18
18
 
19
19
  validates_presence_of :submissions_count,
@@ -84,6 +84,14 @@ class Organization < ApplicationRecord
84
84
  all.select { |it| it.public? || user.has_permission?(role, it.slug) }
85
85
  end
86
86
 
87
+ def title_suffix
88
+ central? ? '' : " - #{book.name}"
89
+ end
90
+
91
+ def site_name
92
+ central? ? 'mumuki' : name
93
+ end
94
+
87
95
  private
88
96
 
89
97
  def ensure_consistent_public_login
@@ -31,7 +31,9 @@ class Submission
31
31
  end
32
32
 
33
33
  def save_results!(results, assignment)
34
- assignment.update! results
34
+ assignment.assign_attributes results
35
+ assignment.increment_attemps!
36
+ assignment.save! results
35
37
  end
36
38
 
37
39
  def notify_results!(results, assignment)
data/app/models/user.rb CHANGED
@@ -76,6 +76,10 @@ class User < ApplicationRecord
76
76
  end
77
77
  end
78
78
 
79
+ def accept_invitation!(invitation)
80
+ make_student_of! invitation.course
81
+ end
82
+
79
83
  def copy_progress_to!(another)
80
84
  transaction do
81
85
  assignments.update_all(submitter_id: another.id)
@@ -1,6 +1,6 @@
1
1
  <div class="col-md-offset-2 col-md-8">
2
2
  <div class="book-header <%= current_user? ? '' : 'logged' %>">
3
- <h1>ム mumuki</h1>
3
+ <h1>ム mumuki<%= Organization.current.title_suffix %></h1>
4
4
 
5
5
  <img src="<%= Organization.current.banner_url %>" alt="<%= Organization.current.name %> - Mumuki">
6
6
  <h2><%= @book.name %></h2>
@@ -0,0 +1,7 @@
1
+ <% expectation = assignment.visible_expectation_results.find { |it| it[:result].failed? } %>
2
+
3
+ <% if expectation %>
4
+ <ul class="results-list">
5
+ <li><%= status_icon(expectation[:result]) %> <%= t_expectation expectation %></li>
6
+ </ul>
7
+ <% end %>
@@ -25,7 +25,6 @@
25
25
  <% if render_feedback?(assignment) %>
26
26
  <div class="results-item">
27
27
  <strong><%= t :feedback %>:</strong>
28
-
29
28
  <div>
30
29
  <%= assignment.feedback_html %>
31
30
  </div>
@@ -50,6 +49,7 @@
50
49
  <%= mail_to contact_email,
51
50
  fa_icon(:bug, text: t(:notify_problem_with_exercise), class: 'fa-fw'),
52
51
  subject: t(:problem_with_exercise, title: @exercise.name),
52
+ body: assignment_help_email_body(assignment),
53
53
  class: 'warning' %>
54
54
  </li>
55
55
  <li>
@@ -63,6 +63,9 @@
63
63
  <%= solution_download_link assignment %>
64
64
  </div>
65
65
 
66
- <%= corollary_box @exercise unless assignment.should_retry? %>
67
-
66
+ <% if assignment.should_retry? %>
67
+ <%= assistance_box assignment %>
68
+ <% else %>
69
+ <%= corollary_box @exercise %>
70
+ <% end %>
68
71
  <%= render partial: 'exercise_solutions/results_button', locals: {assignment: assignment} %>
@@ -1,7 +1,5 @@
1
1
  <img src="/anim_amarillo.svg" alt="">
2
2
  <div class="mu-kids-character-speech-bubble">
3
- <i class="mu-kids-prev-speech fa fa-fw fa-caret-up"></i>
4
- <i class="mu-kids-next-speech fa fa-fw fa-caret-down"></i>
5
3
  <% if @exercise.hint? %>
6
4
  <ul class="mu-kids-character-speech-bubble-tabs">
7
5
  <li class="active" data-target="description" title="<%= t :task %>"><i class="fa fa-fw fa-file-text-o"></i>
@@ -11,8 +9,10 @@
11
9
  </ul>
12
10
  <% end %>
13
11
  <div class="mu-kids-character-speech-bubble-normal">
12
+ <i class="mu-kids-prev-speech fa fa-fw fa-caret-up"></i>
13
+ <i class="mu-kids-next-speech fa fa-fw fa-caret-down"></i>
14
14
  <div class="description"><%= @exercise.description_task %></div>
15
15
  <div class="hint" style="display: none"><%= @exercise.hint_html %></div>
16
16
  </div>
17
- <div class="mu-kids-character-speech-bubble-failed" style="display: none"></div>
17
+ <div class="mu-kids-character-speech-bubble-failed <%= @assignment&.status %>" style="display: none"></div>
18
18
  </div>
@@ -9,9 +9,9 @@
9
9
  <link rel="icon" type="image/x-icon" href="<%= Organization.current.favicon_url %>" data-turbolinks-track="reload">
10
10
  <link rel="stylesheet" type="text/css" href="<%= theme_stylesheet_url %>" data-turbolinks-track="reload">
11
11
 
12
- <meta property="og:site_name" content="mumuki"/>
12
+ <meta property="og:site_name" content="<%= Organization.current.site_name %>" />
13
13
  <meta property="og:title" content="<%= page_title subject %>"/>
14
- <meta property="og:description" content="<%= t :mumuki_short_description %>"/>
14
+ <meta property="og:description" content="<%= Organization.current.central? ? t(:mumuki_short_description) : Organization.current.description %>"/>
15
15
  <meta property="og:type" content="website"/>
16
16
  <meta property="og:image" content="<%= Organization.current.open_graph_image_url %>"/>
17
17
  <meta property="og:url" content="<%= request.original_url %>"/>
@@ -21,12 +21,11 @@
21
21
  <meta name="description" content="<%= t :mumuki_short_description %>"/>
22
22
  <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
23
23
  <%= csrf_meta_tags %>
24
- <script type="text/javascript">
25
- window.mumukiLocale = <%= raw Organization.current.locale_json %>;
26
- </script>
27
24
  <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
28
25
  <script type="text/javascript" src="<%= extension_javascript_url %>" defer data-turbolinks-track="reload"></script>
29
26
  <script type="text/javascript">
27
+ window.mumukiLocale = <%= raw Organization.current.locale_json %>;
28
+ mumuki.locale = '<%= Organization.current.locale %>';
30
29
  moment.locale('<%= Organization.current.locale %>');
31
30
  </script>
32
31
  <%= login_form.header_html %>