mumuki-domain 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3035243b0b927688b208101efda8b3be665d244cb4099faf8b8a1bf3a7aa89e1
4
- data.tar.gz: 417de725ebff9ebfd3687084ba41f66e9a44eb4899976ddc75923b4c80894107
3
+ metadata.gz: 0fe12401571cc46abe567d3337c252fcbf0d9a974f37ad207b3d424086b95b7c
4
+ data.tar.gz: dbc42a3ada461878dd43b5eb8dc14d0ec449349099b01a0b80dbf428f08edcdf
5
5
  SHA512:
6
- metadata.gz: 8e9088e52953f6d5fe90bfc66d8e8f793700166eb63c9ac7c298e17aab8bba59a5336af7aeeb9aad3c63be885f0a45b3f3cc546c38989da9b0c9bf21dbac4c87
7
- data.tar.gz: 81ecbff2d1327923403fe3e89a726519469790e70b82c337c4b91690be00aead66280cf6efe97c3d25bb0502667e9970c70ffbfd9b9600cff2a46d6d091e2c2d
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!
@@ -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
@@ -7,6 +7,7 @@ class Book < Content
7
7
 
8
8
  has_many :exercises, through: :chapters
9
9
  has_many :discussions, through: :exercises
10
+ organic_on :discussions
10
11
 
11
12
  delegate :first_lesson, to: :first_chapter
12
13
 
@@ -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
- subscriptions.where(read: false).map(&:discussion)
35
+ subscriptions_in_organization.where(read: false).map(&:discussion)
32
36
  end
33
37
  end
@@ -5,5 +5,6 @@ module WithDiscussionCreation
5
5
  has_many :discussions, foreign_key: 'initiator_id'
6
6
  include WithDiscussionCreation::Subscription
7
7
  include WithDiscussionCreation::Upvote
8
+ organic_on :discussions
8
9
  end
9
10
  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 slug_parts
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 by_slug_parts!(args)
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
- usages.in_organization(organization).first.try(:parent_item)
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.slice_platform_json(resource_h)
18
+ update! Mumukit::Platform::Course::Helpers.slice_resource_h(resource_h)
19
19
  end
20
20
 
21
21
  def slug=(slug)
@@ -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(:moderator?)
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
- item.used_in?(organization)
45
+ organization == self.organization
45
46
  end
46
47
 
47
48
  def commentable_by?(user)
48
- user&.moderator? || (opened? && user.present?)
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(:moderator?)
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
- authorization_for(user).start! unless user.teacher_here?
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)
@@ -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 slug
68
- "#{guide.slug}/#{bibliotheca_id}"
69
+ def transparent_id
70
+ "#{guide.transparent_id}/#{bibliotheca_id}"
69
71
  end
70
72
 
71
- def slug_parts
72
- guide.slug_parts.merge(bibliotheca_id: bibliotheca_id)
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))
@@ -41,7 +41,7 @@ class Invitation < ApplicationRecord
41
41
  end
42
42
 
43
43
  def unexpired
44
- raise RecordNotFound, "This invitation has already expired" if expired?
44
+ raise Mumuki::Domain::GoneError, "This invitation has already expired" if expired?
45
45
  self
46
46
  end
47
47
 
@@ -25,7 +25,7 @@ class Message < ApplicationRecord
25
25
  end
26
26
 
27
27
  def authorized?(user)
28
- from_user?(user) || user&.moderator?
28
+ from_user?(user) || user&.moderator_here?
29
29
  end
30
30
 
31
31
  def authorize!(user)
@@ -95,12 +95,25 @@ class Organization < ApplicationRecord
95
95
  central? ? 'mumuki' : name
96
96
  end
97
97
 
98
- def ask_for_help_enabled?
99
- report_issue_enabled? || community_link.present? || forum_enabled?
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.slice_platform_json resource_h
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.slice_platform_json json
101
+ update! Mumukit::Platform::User::Helpers.slice_resource_h json
102
102
  end
103
103
 
104
104
  def unsubscribe_from_reminders!
@@ -0,0 +1,6 @@
1
+ class AddSettingsToContent < ActiveRecord::Migration[5.1]
2
+ def change
3
+ add_column :exercises, :settings, :text
4
+ add_column :guides, :settings, :text
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ class AddOrganizationToDiscussion < ActiveRecord::Migration[5.1]
2
+ def change
3
+ add_reference :discussions, :organization, index: true
4
+ end
5
+ end
@@ -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&.moderator?
30
+ count > 0 || user&.moderator_here?
31
31
  end
32
32
 
33
33
  def reachable_statuses_for(user, discussion)
34
- if user.moderator?
34
+ if user.moderator_here?
35
35
  reachable_statuses_for_moderator(discussion)
36
36
  else
37
37
  reachable_statuses_for_initiator(discussion)
@@ -1,5 +1,5 @@
1
1
  module Mumuki
2
2
  module Domain
3
- VERSION = '6.5.1'
3
+ VERSION = '6.6.0'
4
4
  end
5
5
  end
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.5.1
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-03-14 00:00:00.000000000 Z
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: '3.2'
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: '3.2'
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