mumuki-laboratory 6.5.1 → 6.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +22 -7
- data/app/assets/javascripts/application/bridge.js +16 -4
- data/app/assets/javascripts/application/characters.js +1 -1
- data/app/assets/javascripts/application/kids.js +1 -1
- data/app/assets/javascripts/application/messages.js +2 -6
- data/app/assets/javascripts/application/submission.js +1 -1
- data/app/assets/stylesheets/application/modules/_kids.scss +9 -3
- data/app/controllers/application_controller.rb +6 -0
- data/app/controllers/discussions_controller.rb +12 -2
- data/app/controllers/exercises_controller.rb +2 -2
- data/app/controllers/guides_controller.rb +2 -6
- data/app/controllers/topics_controller.rb +5 -0
- data/app/controllers/users_controller.rb +1 -1
- data/app/helpers/discussions_helper.rb +3 -1
- data/app/helpers/editor_tabs_helper.rb +2 -2
- data/app/helpers/email_helper.rb +1 -1
- data/app/helpers/exercise_input_helper.rb +1 -1
- data/app/helpers/links_helper.rb +1 -1
- data/app/helpers/menu_bar_helper.rb +2 -2
- data/app/views/discussions/_message.html.erb +1 -1
- data/app/views/discussions/show.html.erb +1 -1
- data/app/views/layouts/exercise_inputs/forms/_playground_form.html.erb +1 -1
- data/config/routes.rb +7 -2
- data/lib/mumuki/laboratory/locales/pt.yml +1 -1
- data/lib/mumuki/laboratory/version.rb +1 -1
- data/spec/controllers/discussions_controller_spec.rb +38 -1
- data/spec/dummy/db/schema.rb +10 -1
- data/spec/dummy/public/character/animations.json +4 -1
- data/spec/features/dynamic_exam_spec.rb +4 -4
- data/spec/features/exercise_flow_spec.rb +8 -8
- data/spec/features/progressive_tips_spec.rb +1 -1
- data/spec/features/topic_flow_spec.rb +27 -0
- data/spec/helpers/email_helper_spec.rb +1 -1
- data/spec/helpers/exercise_input_helper_spec.rb +6 -1
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0cd1851406b33a479d3c82a2fda1d82c4614bcd11104b577595b1578bc432eab
|
4
|
+
data.tar.gz: 3a28bd64ebdec590b2763bca2f70bd9ff1e85708d49f76b59b9314d7318a1dc4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 22711361229161b1b1cb626516ff7462943b971fda7434fda012da775a9cdc645d7f5be92ad8f3d9f35a588e43a028e1141b8913a6d5a4b7ee891123eccc9da0
|
7
|
+
data.tar.gz: ef931680911d4bfe9de176c7ee54e7692bcfc34260cf415132c14d2c8ec404274a723b1ecd51927b6b465670ae71d4bedd02bb48be1d596843d002e566113db9
|
data/README.md
CHANGED
@@ -177,14 +177,20 @@ which are granted to be safe and stable.
|
|
177
177
|
### Methods
|
178
178
|
|
179
179
|
* `mumuki.bridge.Laboratory`
|
180
|
-
* `
|
181
|
-
* `mumuki.kids
|
182
|
-
* `
|
183
|
-
* `
|
184
|
-
* `
|
185
|
-
* `
|
180
|
+
* `.runTests`
|
181
|
+
* `mumuki.kids`
|
182
|
+
* `registerBlocksAreaScaler`
|
183
|
+
* `registerStateScaler`
|
184
|
+
* `restart`
|
185
|
+
* `scaleBlocksArea`
|
186
|
+
* `scaleState`
|
187
|
+
* `showResult`
|
186
188
|
* `mumuki.locale`
|
187
189
|
* `mumuki.MultipleScenarios`
|
190
|
+
* `scenarios`
|
191
|
+
* `currentScenarioIndex`
|
192
|
+
* `resetIndicators`
|
193
|
+
* `updateIndicators`
|
188
194
|
* `mumuki.version`
|
189
195
|
|
190
196
|
### Kids Call order
|
@@ -194,6 +200,14 @@ which are granted to be safe and stable.
|
|
194
200
|
2. Laboratory Kids Layout Initialization
|
195
201
|
3. Runner Editor HTML
|
196
202
|
|
203
|
+
## Transparent Navigation API Docs
|
204
|
+
|
205
|
+
In order to be able to link content, laboratory exposes slug-based routes that will redirect to the actual
|
206
|
+
content URL in the current organization transparently:
|
207
|
+
|
208
|
+
* `GET <organization-url>/topics/<organization>/<repository>`
|
209
|
+
* `GET <organization-url>/guides/<organization>/<repository>`
|
210
|
+
* `GET <organization-url>/exercies/<organization>/<repository>/<bibliotheca-id>`
|
197
211
|
|
198
212
|
## REST API Docs
|
199
213
|
|
@@ -201,7 +215,7 @@ Before using the API, you must create an `ApiClient` using `rails c`, which will
|
|
201
215
|
|
202
216
|
Before using the API, take a look to the roles hierarchy:
|
203
217
|
|
204
|
-
![roles hierarchy](https://yuml.me/diagram/plain/class/[Owner]%5E-[Janitor],%20[Janitor]%5E-[Headmaster],%20[Headmaster]%5E-[Teacher],%20[Teacher]%5E-[Student],%20,%20[Owner]%5E-[Editor],%20[Editor]%5E-[Writer]).
|
218
|
+
![roles hierarchy](https://yuml.me/diagram/plain/class/[Owner]%5E-[Janitor],[Owner]%5E-[Moderator],%20[Janitor]%5E-[Headmaster],%20[Headmaster]%5E-[Teacher],%20[Teacher]%5E-[Student],%20,%20[Owner]%5E-[Editor],%20[Editor]%5E-[Writer]).
|
205
219
|
|
206
220
|
Permissions are bound to a scope, that states in which context the operation can be performed. Scopes are simply two-level contexts, expressed as slugss `<first>/<second>`, without any explicit semantic. They exact meaning depends on the role:
|
207
221
|
|
@@ -209,6 +223,7 @@ Permissions are bound to a scope, that states in which context the operation can
|
|
209
223
|
* teacher and headmaster: `organization/course`
|
210
224
|
* writer and editor: `organization/content`
|
211
225
|
* janitor: `organization/_`
|
226
|
+
* moderator: `organization/_`
|
212
227
|
* owner: `_/_`
|
213
228
|
|
214
229
|
### Users
|
@@ -37,16 +37,28 @@ var mumuki = mumuki || {};
|
|
37
37
|
});
|
38
38
|
|
39
39
|
Laboratory.prototype = {
|
40
|
-
|
40
|
+
|
41
|
+
// ==========
|
42
|
+
// Public API
|
43
|
+
// ==========
|
44
|
+
|
45
|
+
// Runs tests for the current exercise using the given submission
|
46
|
+
// content.
|
47
|
+
runTests: function(content) {
|
48
|
+
return this._submitSolution({ solution: content });
|
49
|
+
},
|
50
|
+
|
51
|
+
// ===========
|
52
|
+
// Private API
|
53
|
+
// ===========
|
54
|
+
|
55
|
+
_submitSolution: function (solution) {
|
41
56
|
if(lastSubmissionFinishedSuccessfully() && sameAsLastSolution(solution)){
|
42
57
|
return $.Deferred().resolve(lastSubmission.result);
|
43
58
|
} else {
|
44
59
|
return sendNewSolution(solution);
|
45
60
|
}
|
46
61
|
},
|
47
|
-
runTests: function(content) {
|
48
|
-
return this.runLocalTests({ solution: content });
|
49
|
-
}
|
50
62
|
};
|
51
63
|
|
52
64
|
mumuki.bridge = {
|
@@ -120,7 +120,7 @@ mumuki.load(function () {
|
|
120
120
|
mumuki.kids._hideMessageOnCharacterBubble();
|
121
121
|
var $bubble = mumuki.kids._getCharacterBubble();
|
122
122
|
Object.keys(mumuki.kids.resultAction).forEach($bubble.removeClass.bind($bubble));
|
123
|
-
mumuki.presenterCharacter.playAnimation('
|
123
|
+
mumuki.presenterCharacter.playAnimation('talk', mumuki.kids._getCharaterImage());
|
124
124
|
},
|
125
125
|
|
126
126
|
// ===========
|
@@ -1,7 +1,4 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
(function (mumuki) {
|
4
|
-
|
1
|
+
mumuki.load(function () {
|
5
2
|
var Chat = {
|
6
3
|
$body: function () {
|
7
4
|
return $('body')
|
@@ -92,6 +89,5 @@ var mumuki = mumuki || {};
|
|
92
89
|
Chat.setMessagesInterval();
|
93
90
|
mumuki.Chat = Chat;
|
94
91
|
|
95
|
-
}
|
96
|
-
|
92
|
+
})
|
97
93
|
|
@@ -90,7 +90,7 @@ var mumuki = mumuki || {};
|
|
90
90
|
mumuki.editor.syncContent();
|
91
91
|
var solution = getContent();
|
92
92
|
|
93
|
-
bridge.
|
93
|
+
bridge._submitSolution(solution).done(function (data) {
|
94
94
|
resultsBox.success(data, submitButton);
|
95
95
|
}).fail(function () {
|
96
96
|
resultsBox.error(submitButton);
|
@@ -4,6 +4,7 @@ $kids-characters-height: 120px;
|
|
4
4
|
$kids-speech-arrow-size: 10px;
|
5
5
|
$kids-speech-border-color: lighten($mu-color-disabled, 20%);
|
6
6
|
$kids-speech-background-color: #f4f6fb;
|
7
|
+
$kids-scrollbar-background-color: #d7d9dd;
|
7
8
|
|
8
9
|
$kids-speech-border: 1px solid $kids-speech-border-color;
|
9
10
|
|
@@ -94,6 +95,10 @@ $kids-speech-tabs-width: 40px;
|
|
94
95
|
|
95
96
|
}
|
96
97
|
|
98
|
+
.mu-kids-character-animation {
|
99
|
+
width: 120px;
|
100
|
+
}
|
101
|
+
|
97
102
|
.mu-kids-character {
|
98
103
|
position: relative;
|
99
104
|
display: flex;
|
@@ -358,12 +363,13 @@ $kids-speech-tabs-width: 40px;
|
|
358
363
|
ry: 10px !important;
|
359
364
|
}
|
360
365
|
.blocklyScrollbarHandle {
|
361
|
-
|
362
|
-
fill: $kids-speech-background-color !important;
|
366
|
+
fill: $kids-scrollbar-background-color !important;
|
363
367
|
stroke: $kids-speech-border-color !important;
|
364
368
|
stroke-width: 1px !important;
|
365
369
|
}
|
366
|
-
.
|
370
|
+
.blocklyMainWorkspaceScrollbar {
|
371
|
+
display: none;
|
372
|
+
}
|
367
373
|
.blocklyScrollbarHorizontal {
|
368
374
|
display: none;
|
369
375
|
}
|
@@ -54,6 +54,12 @@ class ApplicationController < ActionController::Base
|
|
54
54
|
login_form.button_html I18n.t(:sign_in), options[:class]
|
55
55
|
end
|
56
56
|
|
57
|
+
# redirects to the usage in the current organization for the given content
|
58
|
+
# or raises a not found error if unused
|
59
|
+
def redirect_to_usage(content)
|
60
|
+
raise Mumuki::Domain::NotFoundError unless content.usage_in_organization.try { |usage| redirect_to usage }
|
61
|
+
end
|
62
|
+
|
57
63
|
private
|
58
64
|
|
59
65
|
def login_settings
|
@@ -1,8 +1,14 @@
|
|
1
1
|
class DiscussionsController < ApplicationController
|
2
2
|
include Mumuki::Laboratory::Controllers::Content
|
3
3
|
|
4
|
-
|
4
|
+
# discussions are not enabled for all organitions nor all users
|
5
|
+
# there is no need to validate forum existance when creating; next validation is stronger
|
6
|
+
before_action :validate_forum_enabled!, except: :create
|
7
|
+
before_action :validate_can_create_discusssion!, only: :create
|
8
|
+
|
9
|
+
# users are not allowed to access discussions during exams
|
5
10
|
before_action :validate_not_in_exam!
|
11
|
+
|
6
12
|
before_action :set_debatable, except: [:subscription]
|
7
13
|
before_action :authenticate!, only: [:update, :create]
|
8
14
|
before_action :discussion_filter_params, only: :index
|
@@ -41,7 +47,7 @@ class DiscussionsController < ApplicationController
|
|
41
47
|
private
|
42
48
|
|
43
49
|
def current_content_discussions
|
44
|
-
@debatable.
|
50
|
+
@debatable.discussions_in_organization
|
45
51
|
end
|
46
52
|
|
47
53
|
def set_debatable
|
@@ -69,6 +75,10 @@ class DiscussionsController < ApplicationController
|
|
69
75
|
raise Mumuki::Domain::NotFoundError unless Organization.current.forum_enabled?
|
70
76
|
end
|
71
77
|
|
78
|
+
def validate_can_create_discusssion!
|
79
|
+
raise Mumuki::Domain::NotFoundError unless Organization.current.can_create_discussions?(current_user)
|
80
|
+
end
|
81
|
+
|
72
82
|
def validate_not_in_exam!
|
73
83
|
raise Mumuki::Domain::BlockedForumError if current_user&.currently_in_exam?
|
74
84
|
end
|
@@ -12,8 +12,8 @@ class ExercisesController < ApplicationController
|
|
12
12
|
enable_embedded_rendering
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
16
|
-
redirect_to
|
15
|
+
def show_transparently
|
16
|
+
redirect_to Exercise.find_transparently!(params)
|
17
17
|
end
|
18
18
|
|
19
19
|
private
|
@@ -3,11 +3,7 @@ class GuidesController < ApplicationController
|
|
3
3
|
redirect_to_usage Guide.find_by!(id: params[:id])
|
4
4
|
end
|
5
5
|
|
6
|
-
def
|
7
|
-
redirect_to_usage Guide.
|
8
|
-
end
|
9
|
-
|
10
|
-
def redirect_to_usage(guide)
|
11
|
-
raise Mumuki::Domain::NotFoundError unless guide.usage_in_organization.try { |usage| redirect_to usage }
|
6
|
+
def show_transparently
|
7
|
+
redirect_to_usage Guide.find_transparently!(params)
|
12
8
|
end
|
13
9
|
end
|
@@ -32,7 +32,7 @@ module DiscussionsHelper
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def solve_discussion_params_for(user)
|
35
|
-
if user&.
|
35
|
+
if user&.moderator_here?
|
36
36
|
{ status: :pending_review, sort: :created_at_asc }
|
37
37
|
else
|
38
38
|
{ status: :opened, sort: :created_at_asc }
|
@@ -87,6 +87,8 @@ module DiscussionsHelper
|
|
87
87
|
end
|
88
88
|
|
89
89
|
def new_discussion_link(teaser_text, link_text)
|
90
|
+
return '' unless Organization.current.can_create_discussions?(current_user)
|
91
|
+
|
90
92
|
%Q{
|
91
93
|
<h4>
|
92
94
|
<span>#{t(teaser_text)}</span>
|
@@ -3,8 +3,8 @@ module EditorTabsHelper
|
|
3
3
|
"<li role='presentation'> <a data-target='#visible-extra' aria-controls='visible-extra' role='tab' data-toggle='tab' class='editor-tab'>#{fa_icon 'code'} #{t 'activerecord.attributes.exercise.extra'}</a> </li>".html_safe
|
4
4
|
end
|
5
5
|
|
6
|
-
def console_tab
|
7
|
-
"<li role='presentation'>
|
6
|
+
def console_tab(active: false)
|
7
|
+
"<li role='presentation' class='#{'active' if active}'>
|
8
8
|
<a data-target='#console' aria-controls='console' tabindex='0' role='tab' data-toggle='tab' class='editor-tab'>
|
9
9
|
#{fa_icon 'terminal'}#{t :console }
|
10
10
|
</a>
|
data/app/helpers/email_helper.rb
CHANGED
@@ -44,7 +44,7 @@ module ExerciseInputHelper
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def should_render_need_help_dropdown?(assignment, organization = Organization.current)
|
47
|
-
!assignment.passed? && organization.ask_for_help_enabled?
|
47
|
+
!assignment.passed? && organization.ask_for_help_enabled?(assignment.submitter)
|
48
48
|
end
|
49
49
|
|
50
50
|
def render_submit_button(assignment)
|
data/app/helpers/links_helper.rb
CHANGED
@@ -13,11 +13,11 @@ module MenuBarHelper
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def link_to_classroom
|
16
|
-
link_to_application 'graduation-cap', :
|
16
|
+
link_to_application 'graduation-cap', :classroom_ui, :teacher_here?
|
17
17
|
end
|
18
18
|
|
19
19
|
def link_to_bibliotheca
|
20
|
-
link_to_application :book, :
|
20
|
+
link_to_application :book, :bibliotheca_ui, :writer?
|
21
21
|
end
|
22
22
|
|
23
23
|
def link_to_application(icon, app_name, minimal_permissions)
|
@@ -6,7 +6,7 @@
|
|
6
6
|
<span class="message-date">
|
7
7
|
<%= t(:time_since, time: time_ago_in_words(message.created_at)) %>
|
8
8
|
</span>
|
9
|
-
<% if user.
|
9
|
+
<% if user.moderator_here? %>
|
10
10
|
<%= fa_icon(:star, {'data-toggle': 'tooltip', title: (t :moderator), class: 'moderator-star'}) %>
|
11
11
|
<% end %>
|
12
12
|
<% if message.authorized? current_user %>
|
data/config/routes.rb
CHANGED
@@ -58,10 +58,15 @@ Rails.application.routes.draw do
|
|
58
58
|
get '/messages/errors' => 'messages#errors'
|
59
59
|
|
60
60
|
# Routes by slug
|
61
|
-
get '/guides/:organization/:repository' => 'guides#
|
62
|
-
get '/
|
61
|
+
get '/guides/:organization/:repository' => 'guides#show_transparently', as: :transparent_guide
|
62
|
+
get '/topics/:organization/:repository' => 'topics#show_transparently', as: :transparent_topic
|
63
|
+
get '/exercises/:organization/:repository/:bibliotheca_id' => 'exercises#show_transparently', as: :transparent_exercise
|
64
|
+
|
65
|
+
# Join to course
|
63
66
|
get '/join/:code' => 'invitations#show', as: :invitation
|
64
67
|
post '/join/:code' => 'invitations#join'
|
68
|
+
|
69
|
+
# Notification subscriptions
|
65
70
|
get '/user/unsubscribe' => 'users#unsubscribe'
|
66
71
|
put '/user' => 'users#update', as: :update_user
|
67
72
|
|
@@ -107,7 +107,7 @@ pt:
|
|
107
107
|
joining: Bem-vindo ao curso <strong> %{curso} </strong>!
|
108
108
|
kids_default_success: Você fez muito bem!
|
109
109
|
language: Linguagem
|
110
|
-
languages:
|
110
|
+
languages: Linguagens
|
111
111
|
last_name: Sobrenome
|
112
112
|
last_submission_date: Última solução
|
113
113
|
latest_exercises: Últimos exercícios
|
@@ -1,13 +1,31 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe DiscussionsController, organization_workspace: :test do
|
4
|
-
let(:user) { create(:user) }
|
4
|
+
let(:user) { create(:user, permissions: Mumukit::Auth::Permissions.parse(student: '*')) }
|
5
5
|
let(:exercise) { create(:exercise) }
|
6
6
|
let(:exercise_params) { {debatable_class: 'Exercise', exercise_id: exercise.id} }
|
7
7
|
|
8
8
|
before { set_current_user! user }
|
9
9
|
before { Organization.current.tap { |it| it.forum_enabled = true }.save! }
|
10
10
|
|
11
|
+
describe 'show' do
|
12
|
+
let(:another_orga) { create(:organization, {name: 'another_orga'}) }
|
13
|
+
|
14
|
+
describe 'a discussion from the current orga' do
|
15
|
+
let(:discussion) { create(:discussion, {organization: Organization.current}) }
|
16
|
+
before {get :show, params: exercise_params.merge({id: discussion.id})}
|
17
|
+
|
18
|
+
it {expect(response.status).to eq 200}
|
19
|
+
end
|
20
|
+
|
21
|
+
describe 'a discussion from another orga' do
|
22
|
+
let(:discussion) { create(:discussion, {organization: another_orga}) }
|
23
|
+
before {get :show, params: exercise_params.merge({id: discussion.id})}
|
24
|
+
|
25
|
+
it {expect(response.status).to eq 404}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
11
29
|
describe 'post' do
|
12
30
|
before { allow_any_instance_of(DiscussionsController).to receive(:discussion_params).and_return title: 'A title' }
|
13
31
|
before { post :create, params: exercise_params }
|
@@ -24,4 +42,23 @@ describe DiscussionsController, organization_workspace: :test do
|
|
24
42
|
it { expect { Discussion.find(@discussion_id) }.to raise_exception(ActiveRecord::RecordNotFound) }
|
25
43
|
end
|
26
44
|
end
|
45
|
+
|
46
|
+
describe 'when the minimal role for discussions is teacher' do
|
47
|
+
before { Organization.current.tap { |it| it.forum_discussions_minimal_role = 'teacher' }.save! }
|
48
|
+
|
49
|
+
describe 'and the user does not match the minimal role' do
|
50
|
+
before { post :create, params: exercise_params }
|
51
|
+
|
52
|
+
it { expect(response.status).to eq 404 }
|
53
|
+
end
|
54
|
+
|
55
|
+
describe 'and the user matches the minimal role' do
|
56
|
+
let(:user) { create(:user, permissions: Mumukit::Auth::Permissions.parse(teacher: '*')) }
|
57
|
+
before { allow_any_instance_of(DiscussionsController).to receive(:discussion_params).and_return title: 'A title' }
|
58
|
+
before { post :create, params: exercise_params }
|
59
|
+
|
60
|
+
it { expect(response.status).to eq 302 }
|
61
|
+
it { expect(exercise.discussions.size).to eq 1 }
|
62
|
+
end
|
63
|
+
end
|
27
64
|
end
|
data/spec/dummy/db/schema.rb
CHANGED
@@ -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:
|
13
|
+
ActiveRecord::Schema.define(version: 20190404181724) do
|
14
14
|
|
15
15
|
# These are extensions that must be enabled in order to support this database
|
16
16
|
enable_extension "plpgsql"
|
@@ -105,8 +105,10 @@ ActiveRecord::Schema.define(version: 20190123180147) do
|
|
105
105
|
t.text "query_results"
|
106
106
|
t.text "manual_evaluation_comment"
|
107
107
|
t.integer "upvotes_count", default: 0
|
108
|
+
t.bigint "organization_id"
|
108
109
|
t.index ["initiator_id"], name: "index_discussions_on_initiator_id"
|
109
110
|
t.index ["item_type", "item_id"], name: "index_discussions_on_item_type_and_item_id"
|
111
|
+
t.index ["organization_id"], name: "index_discussions_on_organization_id"
|
110
112
|
end
|
111
113
|
|
112
114
|
create_table "exam_authorizations", force: :cascade do |t|
|
@@ -167,6 +169,7 @@ ActiveRecord::Schema.define(version: 20190123180147) do
|
|
167
169
|
t.text "free_form_editor_source"
|
168
170
|
t.text "teacher_info"
|
169
171
|
t.text "choices"
|
172
|
+
t.text "settings"
|
170
173
|
t.index ["guide_id"], name: "index_exercises_on_guide_id"
|
171
174
|
t.index ["language_id"], name: "index_exercises_on_language_id"
|
172
175
|
end
|
@@ -191,6 +194,7 @@ ActiveRecord::Schema.define(version: 20190123180147) do
|
|
191
194
|
t.text "teacher_info"
|
192
195
|
t.text "sources"
|
193
196
|
t.text "learn_more"
|
197
|
+
t.text "settings"
|
194
198
|
t.index ["name"], name: "index_guides_on_name"
|
195
199
|
t.index ["slug"], name: "index_guides_on_slug", unique: true
|
196
200
|
end
|
@@ -332,4 +336,9 @@ ActiveRecord::Schema.define(version: 20190123180147) do
|
|
332
336
|
t.index ["uid"], name: "index_users_on_uid", unique: true
|
333
337
|
end
|
334
338
|
|
339
|
+
add_foreign_key "chapters", "topics"
|
340
|
+
add_foreign_key "complements", "guides"
|
341
|
+
add_foreign_key "exams", "guides"
|
342
|
+
add_foreign_key "lessons", "guides"
|
343
|
+
add_foreign_key "organizations", "books"
|
335
344
|
end
|
@@ -17,7 +17,7 @@ feature 'Dynamic Exam', organization_workspace: :test do
|
|
17
17
|
|
18
18
|
context 'not logged user' do
|
19
19
|
scenario 'visit exercise by slug' do
|
20
|
-
visit "/exercises/#{problem.
|
20
|
+
visit "/exercises/#{problem.transparent_id}"
|
21
21
|
|
22
22
|
expect(page).to have_text('do f = some_string')
|
23
23
|
end
|
@@ -25,14 +25,14 @@ feature 'Dynamic Exam', organization_workspace: :test do
|
|
25
25
|
|
26
26
|
|
27
27
|
context 'logged user' do
|
28
|
-
scenario 'visit exercise by
|
28
|
+
scenario 'visit exercise by transparent_id' do
|
29
29
|
set_current_user! user
|
30
|
-
visit "/exercises/#{problem.
|
30
|
+
visit "/exercises/#{problem.transparent_id}"
|
31
31
|
|
32
32
|
expect(page).to have_text('do f = some_other_string')
|
33
33
|
|
34
34
|
set_current_user! user2
|
35
|
-
visit "/exercises/#{problem.
|
35
|
+
visit "/exercises/#{problem.transparent_id}"
|
36
36
|
|
37
37
|
expect(page).to have_text('do f = some_string')
|
38
38
|
end
|
@@ -29,13 +29,13 @@ feature 'Exercise Flow', organization_workspace: :test do
|
|
29
29
|
before { reindex_current_organization! }
|
30
30
|
|
31
31
|
context 'inexistent exercise' do
|
32
|
-
scenario 'visit exercise
|
33
|
-
visit "/exercises/#{exercise_not_in_path.
|
32
|
+
scenario 'visit exercise transparently, not in path' do
|
33
|
+
visit "/exercises/#{exercise_not_in_path.transparent_id}"
|
34
34
|
expect(page).to have_text('You may have mistyped the address or the page may have moved')
|
35
35
|
end
|
36
36
|
|
37
|
-
scenario 'visit exercise
|
38
|
-
visit '/exercises/
|
37
|
+
scenario 'visit exercise transparently, unknown exercise' do
|
38
|
+
visit '/exercises/an_exercise_transparent_id'
|
39
39
|
expect(page).to have_text('You may have mistyped the address or the page may have moved')
|
40
40
|
end
|
41
41
|
|
@@ -51,8 +51,8 @@ feature 'Exercise Flow', organization_workspace: :test do
|
|
51
51
|
end
|
52
52
|
|
53
53
|
context 'not logged user' do
|
54
|
-
scenario 'visit exercise
|
55
|
-
visit "/exercises/#{problem_1.
|
54
|
+
scenario 'visit exercise transparently' do
|
55
|
+
visit "/exercises/#{problem_1.transparent_id}"
|
56
56
|
|
57
57
|
expect(page).to have_text('Succ1')
|
58
58
|
expect(page).to_not have_text('Console')
|
@@ -82,8 +82,8 @@ feature 'Exercise Flow', organization_workspace: :test do
|
|
82
82
|
before { set_current_user! user }
|
83
83
|
let(:writer) { create(:user, permissions: {student: 'private/*', writer: 'private/*'}) }
|
84
84
|
|
85
|
-
scenario 'visit exercise
|
86
|
-
visit "/exercises/#{problem_1.
|
85
|
+
scenario 'visit exercise transparently' do
|
86
|
+
visit "/exercises/#{problem_1.transparent_id}"
|
87
87
|
|
88
88
|
expect(page).to have_text('Succ1')
|
89
89
|
expect(page).to have_text('Console')
|
@@ -23,7 +23,7 @@ feature 'Progressive Tips', organization_workspace: :test do
|
|
23
23
|
|
24
24
|
scenario '2 failed submissions' do
|
25
25
|
assignment.update! attempts_count: 2
|
26
|
-
visit "/exercises/#{problem.
|
26
|
+
visit "/exercises/#{problem.transparent_id}"
|
27
27
|
|
28
28
|
expect(page).to have_text('Try this')
|
29
29
|
expect(page).to_not have_text('Try that')
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
feature 'Topic Flow', organization_workspace: :test do
|
4
|
+
let(:user) { create(:user) }
|
5
|
+
let(:problem) { build(:problem) }
|
6
|
+
let(:chapter) { create(:chapter, name: 'Functional Programming', lessons: [ create(:lesson, exercises: [ problem ]) ]) }
|
7
|
+
|
8
|
+
let!(:topic_in_path) { chapter.topic }
|
9
|
+
let(:topic_not_in_path) { create(:topic, name: 'Logic Programming') }
|
10
|
+
|
11
|
+
before { reindex_current_organization! }
|
12
|
+
before { set_current_user! user }
|
13
|
+
|
14
|
+
scenario 'visit topic in path transparently' do
|
15
|
+
visit "/topics/#{topic_in_path.transparent_id}"
|
16
|
+
|
17
|
+
expect(page).to have_text('Functional Programming')
|
18
|
+
expect(page).to have_text('Content')
|
19
|
+
end
|
20
|
+
|
21
|
+
scenario 'visit topic in path not transparently' do
|
22
|
+
visit "/topics/#{topic_not_in_path.transparent_id}"
|
23
|
+
|
24
|
+
expect(page).to_not have_text('Logic Programming')
|
25
|
+
expect(page).to_not have_text('Content Programming')
|
26
|
+
end
|
27
|
+
end
|
@@ -9,6 +9,6 @@ describe EmailHelper, organization_workspace: :test do
|
|
9
9
|
let(:exercise) { create(:problem, name: 'An Exercise', guide: guide, number: 2) }
|
10
10
|
let(:assignment) { exercise.submit_solution!(user, content: 'foo') }
|
11
11
|
|
12
|
-
it { expect(assignment_help_email_body assignment).to eq "Exercise: An Exercise\n\nSolution:\nfoo\n\nStatus: failed\n\nSee http://test.localmumuki.io/exercises/#{exercise.
|
12
|
+
it { expect(assignment_help_email_body assignment).to eq "Exercise: An Exercise\n\nSolution:\nfoo\n\nStatus: failed\n\nSee http://test.localmumuki.io/exercises/#{exercise.transparent_id}\n" }
|
13
13
|
end
|
14
14
|
end
|
@@ -25,7 +25,8 @@ describe ApplicationHelper, organization_workspace: :test do
|
|
25
25
|
end
|
26
26
|
|
27
27
|
describe 'should_render_need_help_dropdown?' do
|
28
|
-
let(:
|
28
|
+
let(:student) { create(:user, permissions: Mumukit::Auth::Permissions.parse(student: "*")) }
|
29
|
+
let(:assignment) { create(:assignment, submitter: student) }
|
29
30
|
|
30
31
|
context 'when the orga has a community link' do
|
31
32
|
let(:organization) { create(:organization, name: 'myorg', community_link: 'com_link') }
|
@@ -39,6 +40,10 @@ describe ApplicationHelper, organization_workspace: :test do
|
|
39
40
|
let(:organization) { create(:organization, name: 'myorg', forum_enabled: true) }
|
40
41
|
it { expect(should_render_need_help_dropdown? assignment, organization).to be true }
|
41
42
|
end
|
43
|
+
context 'when forum enabled but the minimal role for discussions is teacher' do
|
44
|
+
let(:organization) { create(:organization, name: 'myorg', forum_enabled: true, forum_discussions_minimal_role: 'teacher' ) }
|
45
|
+
it { expect(should_render_need_help_dropdown? assignment, organization).to be false }
|
46
|
+
end
|
42
47
|
context 'when ask for help is not enabled' do
|
43
48
|
it { expect(should_render_need_help_dropdown? assignment).to be false }
|
44
49
|
end
|
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: 6.
|
4
|
+
version: 6.6.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: 2019-
|
11
|
+
date: 2019-04-12 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: 6.
|
33
|
+
version: 6.6.1
|
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: 6.
|
40
|
+
version: 6.6.1
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: mumukit-login
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -275,6 +275,7 @@ files:
|
|
275
275
|
- app/controllers/lessons_controller.rb
|
276
276
|
- app/controllers/login_controller.rb
|
277
277
|
- app/controllers/messages_controller.rb
|
278
|
+
- app/controllers/topics_controller.rb
|
278
279
|
- app/controllers/users_controller.rb
|
279
280
|
- app/helpers/application_helper.rb
|
280
281
|
- app/helpers/assets_helper.rb
|
@@ -520,6 +521,7 @@ files:
|
|
520
521
|
- spec/features/profile_flow_spec.rb
|
521
522
|
- spec/features/progressive_tips_spec.rb
|
522
523
|
- spec/features/standard_flow_spec.rb
|
524
|
+
- spec/features/topic_flow_spec.rb
|
523
525
|
- spec/helpers/application_helper_spec.rb
|
524
526
|
- spec/helpers/breadcrumbs_helper_spec.rb
|
525
527
|
- spec/helpers/email_helper_spec.rb
|
@@ -662,6 +664,7 @@ test_files:
|
|
662
664
|
- spec/features/progressive_tips_spec.rb
|
663
665
|
- spec/features/menu_bar_spec.rb
|
664
666
|
- spec/features/home_public_flow_spec.rb
|
667
|
+
- spec/features/topic_flow_spec.rb
|
665
668
|
- spec/features/chapter_spec.rb
|
666
669
|
- spec/features/complements_flow_spec.rb
|
667
670
|
- spec/features/dynamic_exam_spec.rb
|