mumuki-laboratory 5.3.0 → 5.4.0

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