mumuki-domain 6.5.1 → 6.6.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.
- checksums.yaml +4 -4
- data/app/models/application_record.rb +8 -0
- data/app/models/assignment.rb +3 -3
- data/app/models/book.rb +1 -0
- data/app/models/concerns/syncable.rb +0 -5
- data/app/models/concerns/with_discussion_creation/subscription.rb +6 -2
- data/app/models/concerns/with_discussion_creation.rb +1 -0
- data/app/models/concerns/with_discussions.rb +3 -2
- data/app/models/concerns/with_slug.rb +6 -2
- data/app/models/concerns/with_usages.rb +2 -1
- data/app/models/course.rb +1 -1
- data/app/models/discussion.rb +5 -4
- data/app/models/exam.rb +5 -1
- data/app/models/exercise.rb +20 -4
- data/app/models/guide.rb +8 -1
- data/app/models/invitation.rb +1 -1
- data/app/models/message.rb +1 -1
- data/app/models/organization.rb +16 -3
- data/app/models/user.rb +1 -1
- data/db/migrate/20190326152631_add_settings_to_content.rb +6 -0
- data/db/migrate/20190404181724_add_organization_to_discussion.rb +5 -0
- data/lib/mumuki/domain/status/discussion/discussion.rb +2 -2
- data/lib/mumuki/domain/version.rb +1 -1
- metadata +6 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0fe12401571cc46abe567d3337c252fcbf0d9a974f37ad207b3d424086b95b7c
|
|
4
|
+
data.tar.gz: dbc42a3ada461878dd43b5eb8dc14d0ec449349099b01a0b80dbf428f08edcdf
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c228e0879bfc3c4e74013e21a5a8f5e5656e4c2da491b340fd89d24ab7bcc015735d42ad8ca66a2a54662a5ba0a15cf1c1a8ba53d145fbe6c8ce76da69815b81
|
|
7
|
+
data.tar.gz: 6bf1b18815eda0d228a78c6471b6e6d786358dbd1e94ba951d480e830eb03163c83182022bcdd4eeec15fb99ba2e774cfd6947d3f0562ba9277182d297e4a9c3
|
|
@@ -101,6 +101,14 @@ class ApplicationRecord < ActiveRecord::Base
|
|
|
101
101
|
a_hash.with_indifferent_access.slice(*attributes).except(*options[:except])
|
|
102
102
|
end
|
|
103
103
|
|
|
104
|
+
def self.organic_on(*selectors)
|
|
105
|
+
selectors.each do |selector|
|
|
106
|
+
define_method("#{selector}_in_organization") do |organization = Organization.current|
|
|
107
|
+
send(selector).where(organization: organization)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
104
112
|
private
|
|
105
113
|
|
|
106
114
|
def raise_foreign_key_error!
|
data/app/models/assignment.rb
CHANGED
|
@@ -16,7 +16,7 @@ class Assignment < ApplicationRecord
|
|
|
16
16
|
|
|
17
17
|
validates_presence_of :exercise, :submitter
|
|
18
18
|
|
|
19
|
-
delegate :language, :name, :navigable_parent,
|
|
19
|
+
delegate :language, :name, :navigable_parent, :settings,
|
|
20
20
|
:limited?, :input_kids?, :choice?, to: :exercise
|
|
21
21
|
|
|
22
22
|
alias_attribute :status, :submission_status
|
|
@@ -118,11 +118,11 @@ class Assignment < ApplicationRecord
|
|
|
118
118
|
|
|
119
119
|
%w(query try).each do |key|
|
|
120
120
|
name = "run_#{key}!"
|
|
121
|
-
define_method(name) { |params| exercise.send name, params.merge(extra: extra) }
|
|
121
|
+
define_method(name) { |params| exercise.send name, params.merge(extra: extra, settings: settings) }
|
|
122
122
|
end
|
|
123
123
|
|
|
124
124
|
def run_tests!(params)
|
|
125
|
-
exercise.run_tests! params.merge(extra: extra, test: test)
|
|
125
|
+
exercise.run_tests! params.merge(extra: extra, test: test, settings: settings)
|
|
126
126
|
end
|
|
127
127
|
|
|
128
128
|
def to_resource_h
|
data/app/models/book.rb
CHANGED
|
@@ -59,11 +59,6 @@ module Syncable
|
|
|
59
59
|
find_or_initialize_by sync_key_id_field => sync_key_id
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
-
# `locate_resource` is a helpful method that can be used
|
|
63
|
-
# outside the `Mumukit::Sync` context, thus this mixin provide
|
|
64
|
-
# a shorter alias
|
|
65
|
-
alias_method :locate, :locate_resource
|
|
66
|
-
|
|
67
62
|
# `locate!` is similar to `locate`, but fails instead of creating a
|
|
68
63
|
# a new object when not found
|
|
69
64
|
#
|
|
@@ -2,9 +2,13 @@ module WithDiscussionCreation::Subscription
|
|
|
2
2
|
extend ActiveSupport::Concern
|
|
3
3
|
|
|
4
4
|
included do
|
|
5
|
-
has_many :discussions, foreign_key: 'initiator_id'
|
|
6
5
|
has_many :subscriptions
|
|
7
6
|
has_many :watched_discussions, through: :subscriptions, source: :discussion
|
|
7
|
+
organic_on :watched_discussions
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def subscriptions_in_organization
|
|
11
|
+
subscriptions.joins(:discussion).where(discussion: discussions_in_organization)
|
|
8
12
|
end
|
|
9
13
|
|
|
10
14
|
def subscribed_to?(discussion)
|
|
@@ -28,6 +32,6 @@ module WithDiscussionCreation::Subscription
|
|
|
28
32
|
end
|
|
29
33
|
|
|
30
34
|
def unread_discussions
|
|
31
|
-
|
|
35
|
+
subscriptions_in_organization.where(read: false).map(&:discussion)
|
|
32
36
|
end
|
|
33
37
|
end
|
|
@@ -3,10 +3,11 @@ module WithDiscussions
|
|
|
3
3
|
|
|
4
4
|
included do
|
|
5
5
|
has_many :discussions, as: :item, dependent: :destroy
|
|
6
|
+
organic_on :discussions
|
|
6
7
|
end
|
|
7
8
|
|
|
8
|
-
def discuss!(user, discussion)
|
|
9
|
-
discussion.merge!(initiator_id: user.id)
|
|
9
|
+
def discuss!(user, discussion, organization = Organization.current)
|
|
10
|
+
discussion.merge!(initiator_id: user.id, organization: organization)
|
|
10
11
|
discussion.merge!(submission: submission_for(user)) if submission_for(user).present?
|
|
11
12
|
created_discussion = discussions.create discussion
|
|
12
13
|
user.subscribe_to! created_discussion
|
|
@@ -8,11 +8,15 @@ module WithSlug
|
|
|
8
8
|
before_create :normalize_slug!
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
def
|
|
11
|
+
def transparent_params
|
|
12
12
|
org, repo = slug.split('/')
|
|
13
13
|
{organization: org, repository: repo}
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
+
def transparent_id
|
|
17
|
+
slug
|
|
18
|
+
end
|
|
19
|
+
|
|
16
20
|
def normalize_slug!
|
|
17
21
|
self.slug = self.slug.to_mumukit_slug.normalize.to_s
|
|
18
22
|
end
|
|
@@ -40,7 +44,7 @@ module WithSlug
|
|
|
40
44
|
all.select { |it| !it.private? || permissions&.writer?(it.slug) }
|
|
41
45
|
end
|
|
42
46
|
|
|
43
|
-
def
|
|
47
|
+
def find_transparently!(args)
|
|
44
48
|
find_by!(slug: "#{args[:organization]}/#{args[:repository]}")
|
|
45
49
|
end
|
|
46
50
|
|
|
@@ -4,10 +4,11 @@ module WithUsages
|
|
|
4
4
|
included do
|
|
5
5
|
has_many :usages, as: :item
|
|
6
6
|
before_destroy :ensure_unused!
|
|
7
|
+
organic_on :usages
|
|
7
8
|
end
|
|
8
9
|
|
|
9
10
|
def usage_in_organization(organization = Organization.current)
|
|
10
|
-
|
|
11
|
+
usages_in_organization(organization).first.try(:parent_item)
|
|
11
12
|
end
|
|
12
13
|
|
|
13
14
|
def usage_in_organization_of_type(type, organization = Organization.current)
|
data/app/models/course.rb
CHANGED
|
@@ -15,7 +15,7 @@ class Course < ApplicationRecord
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def import_from_resource_h!(resource_h)
|
|
18
|
-
update! Mumukit::Platform::Course::Helpers.
|
|
18
|
+
update! Mumukit::Platform::Course::Helpers.slice_resource_h(resource_h)
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
def slug=(slug)
|
data/app/models/discussion.rb
CHANGED
|
@@ -5,6 +5,7 @@ class Discussion < ApplicationRecord
|
|
|
5
5
|
has_many :messages, -> { order(:created_at) }, dependent: :destroy
|
|
6
6
|
belongs_to :initiator, class_name: 'User'
|
|
7
7
|
belongs_to :exercise, foreign_type: :exercise, foreign_key: 'item_id'
|
|
8
|
+
belongs_to :organization
|
|
8
9
|
has_many :subscriptions
|
|
9
10
|
has_many :upvotes
|
|
10
11
|
|
|
@@ -23,7 +24,7 @@ class Discussion < ApplicationRecord
|
|
|
23
24
|
delegate :to_discussion_status, to: :status
|
|
24
25
|
|
|
25
26
|
scope :for_user, -> (user) do
|
|
26
|
-
if user.try(:
|
|
27
|
+
if user.try(:moderator_here?)
|
|
27
28
|
all
|
|
28
29
|
else
|
|
29
30
|
where.not(status: :closed).where.not(status: :pending_review).or(where(initiator: user))
|
|
@@ -41,11 +42,11 @@ class Discussion < ApplicationRecord
|
|
|
41
42
|
end
|
|
42
43
|
|
|
43
44
|
def used_in?(organization)
|
|
44
|
-
|
|
45
|
+
organization == self.organization
|
|
45
46
|
end
|
|
46
47
|
|
|
47
48
|
def commentable_by?(user)
|
|
48
|
-
user&.
|
|
49
|
+
user&.moderator_here? || (opened? && user.present?)
|
|
49
50
|
end
|
|
50
51
|
|
|
51
52
|
def subscribable?
|
|
@@ -87,7 +88,7 @@ class Discussion < ApplicationRecord
|
|
|
87
88
|
end
|
|
88
89
|
|
|
89
90
|
def authorized?(user)
|
|
90
|
-
initiator?(user) || user.try(:
|
|
91
|
+
initiator?(user) || user.try(:moderator_here?)
|
|
91
92
|
end
|
|
92
93
|
|
|
93
94
|
def initiator?(user)
|
data/app/models/exam.rb
CHANGED
|
@@ -73,7 +73,11 @@ class Exam < ApplicationRecord
|
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
def start!(user)
|
|
76
|
-
|
|
76
|
+
return if user.teacher_here?
|
|
77
|
+
|
|
78
|
+
authorization = authorization_for(user)
|
|
79
|
+
raise Mumuki::Domain::ForbiddenError unless authorization
|
|
80
|
+
authorization.start!
|
|
77
81
|
end
|
|
78
82
|
|
|
79
83
|
def started?(user)
|
data/app/models/exercise.rb
CHANGED
|
@@ -25,6 +25,8 @@ class Exercise < ApplicationRecord
|
|
|
25
25
|
defaults { self.submissions_count = 0 }
|
|
26
26
|
|
|
27
27
|
serialize :choices, Array
|
|
28
|
+
serialize :settings, Hash
|
|
29
|
+
|
|
28
30
|
validates_presence_of :submissions_count,
|
|
29
31
|
:guide, :bibliotheca_id
|
|
30
32
|
|
|
@@ -64,12 +66,12 @@ class Exercise < ApplicationRecord
|
|
|
64
66
|
[language&.name, *tag_list].compact
|
|
65
67
|
end
|
|
66
68
|
|
|
67
|
-
def
|
|
68
|
-
"#{guide.
|
|
69
|
+
def transparent_id
|
|
70
|
+
"#{guide.transparent_id}/#{bibliotheca_id}"
|
|
69
71
|
end
|
|
70
72
|
|
|
71
|
-
def
|
|
72
|
-
guide.
|
|
73
|
+
def transparent_params
|
|
74
|
+
guide.transparent_params.merge(bibliotheca_id: bibliotheca_id)
|
|
73
75
|
end
|
|
74
76
|
|
|
75
77
|
def friendly
|
|
@@ -116,6 +118,7 @@ class Exercise < ApplicationRecord
|
|
|
116
118
|
free_form_editor_source initial_state final_state))
|
|
117
119
|
.merge(id: bibliotheca_id, language: language_resource_h, type: type.underscore)
|
|
118
120
|
.merge(expectations: self[:expectations])
|
|
121
|
+
.merge(settings: self[:settings])
|
|
119
122
|
.merge(RANDOMIZED_FIELDS.map { |it| [it, self[it]] }.to_h)
|
|
120
123
|
.symbolize_keys
|
|
121
124
|
.compact
|
|
@@ -193,6 +196,19 @@ class Exercise < ApplicationRecord
|
|
|
193
196
|
.map { |name, content| Mumuki::Domain::File.new name, content }
|
|
194
197
|
end
|
|
195
198
|
|
|
199
|
+
def self.find_transparently!(params)
|
|
200
|
+
Guide.find_transparently!(params).locate_exercise! params[:bibliotheca_id]
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def self.locate!(slug_and_bibliotheca_id)
|
|
204
|
+
slug, bibliotheca_id = slug_and_bibliotheca_id
|
|
205
|
+
Guide.locate!(slug).locate_exercise! bibliotheca_id
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def settings
|
|
209
|
+
guide.settings.deep_merge super
|
|
210
|
+
end
|
|
211
|
+
|
|
196
212
|
private
|
|
197
213
|
|
|
198
214
|
def evaluation_class
|
data/app/models/guide.rb
CHANGED
|
@@ -8,6 +8,8 @@ class Guide < Content
|
|
|
8
8
|
numbered :exercises
|
|
9
9
|
has_many :exercises, -> { order(number: :asc) }, dependent: :destroy
|
|
10
10
|
|
|
11
|
+
serialize :settings, Hash
|
|
12
|
+
|
|
11
13
|
self.inheritance_column = nil
|
|
12
14
|
|
|
13
15
|
enum type: [:learning, :practice]
|
|
@@ -57,6 +59,11 @@ class Guide < Content
|
|
|
57
59
|
stats_for(user).done?
|
|
58
60
|
end
|
|
59
61
|
|
|
62
|
+
# Finds an exercise by bibliotheca_id within this guide
|
|
63
|
+
def locate_exercise!(bibliotheca_id)
|
|
64
|
+
exercises.find_by!(bibliotheca_id: bibliotheca_id)
|
|
65
|
+
end
|
|
66
|
+
|
|
60
67
|
def import_from_resource_h!(resource_h)
|
|
61
68
|
self.assign_attributes whitelist_attributes(resource_h)
|
|
62
69
|
self.language = Language.for_name(resource_h.dig(:language, :name))
|
|
@@ -80,7 +87,7 @@ class Guide < Content
|
|
|
80
87
|
end
|
|
81
88
|
|
|
82
89
|
def to_resource_h
|
|
83
|
-
as_json(only: %i(beta type id_format private expectations corollary teacher_info sources learn_more authors collaborators extra))
|
|
90
|
+
as_json(only: %i(beta type id_format private expectations corollary teacher_info sources learn_more authors collaborators extra settings))
|
|
84
91
|
.symbolize_keys
|
|
85
92
|
.merge(super)
|
|
86
93
|
.merge(exercises: exercises.map(&:to_resource_h))
|
data/app/models/invitation.rb
CHANGED
data/app/models/message.rb
CHANGED
data/app/models/organization.rb
CHANGED
|
@@ -95,12 +95,25 @@ class Organization < ApplicationRecord
|
|
|
95
95
|
central? ? 'mumuki' : name
|
|
96
96
|
end
|
|
97
97
|
|
|
98
|
-
|
|
99
|
-
|
|
98
|
+
# Tells if the given user can
|
|
99
|
+
# ask for help in this organization
|
|
100
|
+
#
|
|
101
|
+
# Warning: this method does not strictly check user's permission
|
|
102
|
+
def ask_for_help_enabled?(user)
|
|
103
|
+
report_issue_enabled? || community_link.present? || can_create_discussions?(user)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Tells if the given user can
|
|
107
|
+
# create discussion in this organization
|
|
108
|
+
#
|
|
109
|
+
# This is true only when this organization has a forum and the user
|
|
110
|
+
# has the discusser pseudo-permission
|
|
111
|
+
def can_create_discussions?(user)
|
|
112
|
+
forum_enabled? && user.discusser_of?(self)
|
|
100
113
|
end
|
|
101
114
|
|
|
102
115
|
def import_from_resource_h!(resource_h)
|
|
103
|
-
attrs = Mumukit::Platform::Organization::Helpers.
|
|
116
|
+
attrs = Mumukit::Platform::Organization::Helpers.slice_resource_h resource_h
|
|
104
117
|
attrs[:book] = Book.locate! attrs[:book]
|
|
105
118
|
update! attrs
|
|
106
119
|
end
|
data/app/models/user.rb
CHANGED
|
@@ -98,7 +98,7 @@ class User < ApplicationRecord
|
|
|
98
98
|
end
|
|
99
99
|
|
|
100
100
|
def import_from_resource_h!(json)
|
|
101
|
-
update! Mumukit::Platform::User::Helpers.
|
|
101
|
+
update! Mumukit::Platform::User::Helpers.slice_resource_h json
|
|
102
102
|
end
|
|
103
103
|
|
|
104
104
|
def unsubscribe_from_reminders!
|
|
@@ -27,11 +27,11 @@ module Mumuki::Domain::Status::Discussion
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def should_be_shown?(count, user)
|
|
30
|
-
count > 0 || user&.
|
|
30
|
+
count > 0 || user&.moderator_here?
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def reachable_statuses_for(user, discussion)
|
|
34
|
-
if user.
|
|
34
|
+
if user.moderator_here?
|
|
35
35
|
reachable_statuses_for_moderator(discussion)
|
|
36
36
|
else
|
|
37
37
|
reachable_statuses_for_initiator(discussion)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mumuki-domain
|
|
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 Leonardo 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
|
|
@@ -142,14 +142,14 @@ dependencies:
|
|
|
142
142
|
requirements:
|
|
143
143
|
- - "~>"
|
|
144
144
|
- !ruby/object:Gem::Version
|
|
145
|
-
version: '
|
|
145
|
+
version: '4.2'
|
|
146
146
|
type: :runtime
|
|
147
147
|
prerelease: false
|
|
148
148
|
version_requirements: !ruby/object:Gem::Requirement
|
|
149
149
|
requirements:
|
|
150
150
|
- - "~>"
|
|
151
151
|
- !ruby/object:Gem::Version
|
|
152
|
-
version: '
|
|
152
|
+
version: '4.2'
|
|
153
153
|
- !ruby/object:Gem::Dependency
|
|
154
154
|
name: mumukit-sync
|
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -535,6 +535,8 @@ files:
|
|
|
535
535
|
- db/migrate/20190123180139_add_sources_section.rb
|
|
536
536
|
- db/migrate/20190123180147_add_learn_more_section.rb
|
|
537
537
|
- db/migrate/20190312152901_add_content_fk.rb
|
|
538
|
+
- db/migrate/20190326152631_add_settings_to_content.rb
|
|
539
|
+
- db/migrate/20190404181724_add_organization_to_discussion.rb
|
|
538
540
|
- lib/mumuki/domain.rb
|
|
539
541
|
- lib/mumuki/domain/engine.rb
|
|
540
542
|
- lib/mumuki/domain/evaluation.rb
|