mumuki-domain 9.12.0 → 9.14.1

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: d20f0f5d9cfe938c4a33f561f9e4df1e5e84ab18296fda644038b87166d631fc
4
- data.tar.gz: bf7adb112a37b84a8fb4f8fa7dc74db1c5df29dc0d8763005256e98e381b7951
3
+ metadata.gz: b312037c43def7204d8591702f2002340539b6f66df217e4bf8b35e5a0392785
4
+ data.tar.gz: f417d2c02346fcd6e2460c246d38f0a9c4eb1ac8a5162bf72bbe00f983326bec
5
5
  SHA512:
6
- metadata.gz: fbcc0dcdd8fd5134d463f61f18da8dfc872b7142f221f16ea3d99dac828bdbcf628d47b924559ac40e3224597955ad575720dc318b385297509cc3364b5c9897
7
- data.tar.gz: 4a2a358f2b888268d4193b0b9b50873702ac293430c26e5d64576f7c02b185fc4263cbc0b30c02758c611240e9646bb917277ed5ec37951521762db028f0fb52
6
+ metadata.gz: 5786a0b215fee7332099994a47e13b24d2f8a66647ede57aaa67bdcdd2e77433e471d8dc5f7ef48369bea06fb2636f900ed365d24b3b1549f0cef3c48d316f25
7
+ data.tar.gz: 282e425ad11463f7c0945b323327f17addc7997e60f191a034b07317907e0156918fcd667786f4905b89682abe9bb2090148fab392aebd9f99c6c0ab1320be2b
@@ -1,6 +1,8 @@
1
1
  class ApplicationRecord < ActiveRecord::Base
2
2
  self.abstract_class = true
3
3
 
4
+ include WithPgLock
5
+
4
6
  delegate :whitelist_attributes, to: :class
5
7
 
6
8
  def self.teaser_on(*args)
@@ -123,7 +123,7 @@ class Assignment < Progress
123
123
  end
124
124
 
125
125
  def content=(content)
126
- if exercise.solvable?
126
+ if content.present? && exercise.solvable?
127
127
  self.solution = exercise.single_choice? ? exercise.choice_index_for(content) : content
128
128
  end
129
129
  end
@@ -0,0 +1,26 @@
1
+ class Organization::Status::Base
2
+
3
+ attr_reader :organization
4
+
5
+ implements :teacher_access_mode, :student_access_mode, :ex_student_access_mode, :outsider_access_mode, :validate!
6
+
7
+ def initialize(organization)
8
+ @organization = organization
9
+ end
10
+
11
+ def access_mode(user)
12
+ if user&.teacher_of? organization
13
+ teacher_access_mode(user)
14
+ elsif user&.student_of? organization
15
+ student_access_mode(user)
16
+ elsif user&.ex_student_of? organization
17
+ ex_student_access_mode(user)
18
+ else
19
+ outsider_access_mode(user)
20
+ end
21
+ end
22
+
23
+ def validate!(user = nil)
24
+ validate_enabled! unless user
25
+ end
26
+ end
@@ -0,0 +1,27 @@
1
+ class Organization::Status::Disabled < Organization::Status::Base
2
+
3
+ def teacher_access_mode(user)
4
+ OrganizationAccessMode::Full.new user, organization
5
+ end
6
+
7
+ def student_access_mode(user)
8
+ OrganizationAccessMode::ReadOnly.new user, organization, :faqs, :profile, :exercises, :discussions
9
+ end
10
+
11
+ def ex_student_access_mode(user)
12
+ OrganizationAccessMode::ReadOnly.new user, organization, :faqs, :profile
13
+ end
14
+
15
+ def outsider_access_mode(user)
16
+ if organization.public?
17
+ OrganizationAccessMode::Gone.new user, organization
18
+ else
19
+ OrganizationAccessMode::Forbidden.new user, organization
20
+ end
21
+ end
22
+
23
+ def validate_enabled!
24
+ raise Mumuki::Domain::DisabledOrganizationError
25
+ end
26
+ end
27
+
@@ -0,0 +1,25 @@
1
+ class Organization::Status::Enabled < Organization::Status::Base
2
+
3
+ def teacher_access_mode(user)
4
+ OrganizationAccessMode::Full.new user, organization
5
+ end
6
+
7
+ def student_access_mode(user)
8
+ OrganizationAccessMode::Full.new user, organization
9
+ end
10
+
11
+ def ex_student_access_mode(user)
12
+ OrganizationAccessMode::ReadOnly.new user, organization, :faqs, :profile, :discussions, exercises: :submitted
13
+ end
14
+
15
+ def outsider_access_mode(user)
16
+ if organization.public?
17
+ OrganizationAccessMode::Full.new user, organization
18
+ else
19
+ OrganizationAccessMode::Forbidden.new user, organization
20
+ end
21
+ end
22
+
23
+ def validate_enabled!
24
+ end
25
+ end
@@ -0,0 +1,26 @@
1
+ class Organization::Status::InPreparation < Organization::Status::Base
2
+
3
+ def teacher_access_mode(user)
4
+ OrganizationAccessMode::Full.new user, organization
5
+ end
6
+
7
+ def student_access_mode(user)
8
+ OrganizationAccessMode::ComingSoon.new user, organization
9
+ end
10
+
11
+ def ex_student_access_mode(user)
12
+ OrganizationAccessMode::Forbidden.new user, organization
13
+ end
14
+
15
+ def outsider_access_mode(user)
16
+ if organization.public?
17
+ OrganizationAccessMode::ComingSoon.new user, organization
18
+ else
19
+ OrganizationAccessMode::Forbidden.new user, organization
20
+ end
21
+ end
22
+
23
+ def validate_enabled!
24
+ raise Mumuki::Domain::UnpreparedOrganizationError
25
+ end
26
+ end
@@ -30,4 +30,10 @@ module WithAssignments
30
30
  def assignment_for(user, organization=Organization.current)
31
31
  find_assignment_for(user, organization) || user.build_assignment(self, organization)
32
32
  end
33
+
34
+ def has_progress_for?(user, organization)
35
+ user.present? && find_assignment_for(user, organization).present?
36
+ end
37
+
38
+
33
39
  end
@@ -0,0 +1,30 @@
1
+ module WithOrganizationStatus
2
+
3
+ delegate :validate_enabled!, :access_mode, to: :status
4
+
5
+ def status
6
+ @status ||= _status
7
+ end
8
+
9
+ def validate_active!
10
+ status.validate!
11
+ end
12
+
13
+ def validate_active_for!(user)
14
+ status.validate!(user)
15
+ access_mode(user).validate_active!
16
+ end
17
+
18
+ private
19
+
20
+ def _status
21
+ if disabled?
22
+ Organization::Status::Disabled.new self
23
+ elsif in_preparation?
24
+ Organization::Status::InPreparation.new self
25
+ else
26
+ Organization::Status::Enabled.new self
27
+ end
28
+ end
29
+
30
+ end
@@ -7,6 +7,10 @@ module WithProgress
7
7
  progress_for(user, organization).completion_percentage
8
8
  end
9
9
 
10
+ def has_progress_for?(user, organization)
11
+ progress_for(user, organization).persisted?
12
+ end
13
+
10
14
  def dirty_progresses!
11
15
  Indicator.dirty_by_content_change! self
12
16
  end
@@ -1,6 +1,5 @@
1
1
  module WithReminders
2
2
  extend ActiveSupport::Concern
3
- include WithPgLock
4
3
 
5
4
  def build_reminder
6
5
  mailer = UserMailer.new
@@ -1,5 +1,4 @@
1
1
  class ExamRegistration < ApplicationRecord
2
- include WithPgLock
3
2
  include WithTimedEnablement
4
3
  include TerminalNavigation
5
4
 
@@ -77,6 +76,14 @@ class ExamRegistration < ApplicationRecord
77
76
  authorization_criterion.meets_criterion?(user, organization)
78
77
  end
79
78
 
79
+ def multiple_options?
80
+ exams.count > 1
81
+ end
82
+
83
+ def ended?
84
+ end_time.past?
85
+ end
86
+
80
87
  private
81
88
 
82
89
  def notify_registree!(registree)
@@ -6,6 +6,7 @@ class Organization < ApplicationRecord
6
6
  include Mumukit::Login::OrganizationHelpers
7
7
 
8
8
  include WithTargetAudience
9
+ include WithOrganizationStatus
9
10
 
10
11
  serialize :profile, Mumuki::Domain::Organization::Profile
11
12
  serialize :settings, Mumuki::Domain::Organization::Settings
@@ -0,0 +1,53 @@
1
+ class OrganizationAccessMode::Base
2
+ attr_reader :user, :organization
3
+
4
+ def initialize(user, organization)
5
+ @user = user
6
+ @organization = organization
7
+ end
8
+
9
+ def validate_active!
10
+ end
11
+
12
+ def faqs_here?
13
+ organization.faqs.present?
14
+ end
15
+
16
+ def submit_solutions_here?
17
+ false
18
+ end
19
+
20
+ def resolve_discussions_here?
21
+ false
22
+ end
23
+
24
+ def discuss_here?
25
+ organization.forum_enabled? && user.discusser_of?(organization) &&
26
+ user.trusted_as_discusser_in?(organization) && !user.banned_from_forum?
27
+ end
28
+
29
+ def show_discussion_element?
30
+ false
31
+ end
32
+
33
+ def show_content_element?
34
+ false
35
+ end
36
+
37
+ def restore_indicators?(_content)
38
+ false
39
+ end
40
+
41
+ def read_only?
42
+ false
43
+ end
44
+
45
+ def validate_discuss_here!(_discussion)
46
+ raise Mumuki::Domain::ForbiddenError
47
+ end
48
+
49
+ def validate_content_here!(content)
50
+ raise Mumuki::Domain::ForbiddenError unless show_content?(content)
51
+ end
52
+ end
53
+
@@ -0,0 +1,5 @@
1
+ class OrganizationAccessMode::ComingSoon < OrganizationAccessMode::Forbidden
2
+ def validate_active!
3
+ raise Mumuki::Domain::UnpreparedOrganizationError
4
+ end
5
+ end
@@ -0,0 +1,22 @@
1
+ class OrganizationAccessMode::Forbidden < OrganizationAccessMode::Base
2
+ def validate_active!
3
+ raise Mumuki::Domain::ForbiddenError if organization.private? && user.present?
4
+ end
5
+
6
+ def faqs_here?
7
+ false
8
+ end
9
+
10
+ def profile_here?
11
+ false
12
+ end
13
+
14
+ def discuss_here?
15
+ false
16
+ end
17
+
18
+ def show_content?(_content)
19
+ false
20
+ end
21
+
22
+ end
@@ -0,0 +1,28 @@
1
+ class OrganizationAccessMode::Full < OrganizationAccessMode::Base
2
+ def profile_here?
3
+ true
4
+ end
5
+
6
+ def submit_solutions_here?
7
+ true
8
+ end
9
+
10
+ def show_discussion_element?
11
+ true
12
+ end
13
+
14
+ def resolve_discussions_here?
15
+ discuss_here?
16
+ end
17
+
18
+ def validate_discuss_here!(_discussion)
19
+ end
20
+
21
+ def show_content?(_content)
22
+ true
23
+ end
24
+
25
+ def show_content_element?
26
+ true
27
+ end
28
+ end
@@ -0,0 +1,5 @@
1
+ class OrganizationAccessMode::Gone < OrganizationAccessMode::Forbidden
2
+ def validate_active!
3
+ raise Mumuki::Domain::DisabledOrganizationError
4
+ end
5
+ end
@@ -0,0 +1,41 @@
1
+ class OrganizationAccessMode::ReadOnly < OrganizationAccessMode::Base
2
+ def initialize(user, organization, *global_scopes, **specific_scopes)
3
+ super user, organization
4
+ @scopes = global_scopes.map { |scope| [scope, :all] }.to_h.merge specific_scopes
5
+ end
6
+
7
+ def faqs_here?
8
+ has_scope(:faqs) && super
9
+ end
10
+
11
+ def profile_here?
12
+ has_scope(:profile)
13
+ end
14
+
15
+ def discuss_here?
16
+ has_scope(:discussions) && super
17
+ end
18
+
19
+ def validate_discuss_here!(discussion)
20
+ super(discussion) unless discussion&.initiator == user
21
+ end
22
+
23
+ def show_content?(content)
24
+ has_scope(:exercises) ||
25
+ (has_scope(:exercises, :submitted) && content.has_progress_for?(user, organization))
26
+ end
27
+
28
+ def restore_indicators?(book)
29
+ !book.has_progress_for?(user, organization) && user.has_assignments_in_organization?(organization)
30
+ end
31
+
32
+ def read_only?
33
+ true
34
+ end
35
+
36
+ private
37
+
38
+ def has_scope(key, value = :all)
39
+ @scopes[key] == value
40
+ end
41
+ end
@@ -0,0 +1,2 @@
1
+ module OrganizationAccessMode
2
+ end
data/app/models/user.rb CHANGED
@@ -54,7 +54,7 @@ class User < ApplicationRecord
54
54
  resource_fields :uid, :social_id, :email, :permissions, :verified_first_name, :verified_last_name, *profile_fields
55
55
  with_temporary_token :delete_account_token
56
56
 
57
- organic_on :notifications
57
+ organic_on :notifications, :assignments
58
58
 
59
59
  def last_lesson
60
60
  last_guide.try(:lesson)
@@ -64,10 +64,6 @@ class User < ApplicationRecord
64
64
  messages.where('assignments.organization': organization)
65
65
  end
66
66
 
67
- def notifications_in_organization(organization = Organization.current)
68
- notifications.where(organization: organization)
69
- end
70
-
71
67
  def passed_submissions_count_in(organization)
72
68
  assignments.where(top_submission_status: Mumuki::Domain::Status::Submission::Passed.to_i, organization: organization).count
73
69
  end
@@ -111,6 +107,16 @@ class User < ApplicationRecord
111
107
  end
112
108
  end
113
109
 
110
+ def restore_organization_progress!(organization)
111
+ assignments_in_organization(organization).each do |assignment|
112
+ assignment.tap(&:parent).save!
113
+ end
114
+ end
115
+
116
+ def has_assignments_in_organization?(organization)
117
+ assignments_in_organization(organization).exists?
118
+ end
119
+
114
120
  def accept_invitation!(invitation)
115
121
  make_student_of! invitation.course_slug
116
122
  end
@@ -201,7 +207,7 @@ class User < ApplicationRecord
201
207
  # This is true only when this organization has the forum enabled and the user
202
208
  # has the discusser pseudo-permission and the discusser is trusted
203
209
  def can_discuss_in?(organization)
204
- organization.forum_enabled? && discusser_of?(organization) && trusted_as_discusser_in?(organization) && !banned_from_forum?
210
+ organization.access_mode(self).discuss_here?
205
211
  end
206
212
 
207
213
  def trusted_as_discusser_in?(organization)
@@ -283,6 +289,10 @@ class User < ApplicationRecord
283
289
  }
284
290
  end
285
291
 
292
+ def solved_any_exercises?(organization = Organization.current)
293
+ Assignment.exists?(organization: organization, submitter: self)
294
+ end
295
+
286
296
  def save_and_notify!
287
297
  save!
288
298
  notify_permissions_changed!
@@ -52,11 +52,6 @@ module Mumuki::Domain::Helpers::Organization
52
52
  Mumukit::Platform.application.organic_domain(name)
53
53
  end
54
54
 
55
- def validate_active!
56
- raise Mumuki::Domain::DisabledOrganizationError if disabled?
57
- raise Mumuki::Domain::UnpreparedOrganizationError if in_preparation?
58
- end
59
-
60
55
  ## API Exposure
61
56
 
62
57
  def to_param
@@ -16,6 +16,22 @@ module Mumuki::Domain
16
16
  false
17
17
  end
18
18
 
19
+ def ex_student_of?(*)
20
+ false
21
+ end
22
+
23
+ def ex_student_here?
24
+ false
25
+ end
26
+
27
+ def student_of?(*)
28
+ false
29
+ end
30
+
31
+ def student_here?
32
+ false
33
+ end
34
+
19
35
  def teacher_here?
20
36
  false
21
37
  end
@@ -28,7 +28,7 @@ class Mumuki::Domain::Organization::Settings < Mumukit::Platform::Model
28
28
  end
29
29
 
30
30
  def forum_discussions_minimal_role
31
- (@forum_discussions_minimal_role || 'student').to_sym
31
+ (@forum_discussions_minimal_role || 'ex_student').to_sym
32
32
  end
33
33
 
34
34
  def disabled_from=(disabled_from)
@@ -1,5 +1,5 @@
1
1
  module Mumuki
2
2
  module Domain
3
- VERSION = '9.12.0'
3
+ VERSION = '9.14.1'
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: 9.12.0
4
+ version: 9.14.1
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: 2021-08-12 00:00:00.000000000 Z
11
+ date: 2021-09-07 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: '7.9'
33
+ version: '7.11'
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: '7.9'
40
+ version: '7.11'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: mumukit-assistant
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -265,6 +265,10 @@ files:
265
265
  - app/models/concerns/navigation/siblings_navigation.rb
266
266
  - app/models/concerns/navigation/terminal_navigation.rb
267
267
  - app/models/concerns/onomastic.rb
268
+ - app/models/concerns/organization/status/base.rb
269
+ - app/models/concerns/organization/status/disabled.rb
270
+ - app/models/concerns/organization/status/enabled.rb
271
+ - app/models/concerns/organization/status/in_preparation.rb
268
272
  - app/models/concerns/submittable/confirmable.rb
269
273
  - app/models/concerns/submittable/queriable.rb
270
274
  - app/models/concerns/submittable/questionable.rb
@@ -292,6 +296,7 @@ files:
292
296
  - app/models/concerns/with_name.rb
293
297
  - app/models/concerns/with_notifications.rb
294
298
  - app/models/concerns/with_number.rb
299
+ - app/models/concerns/with_organization_status.rb
295
300
  - app/models/concerns/with_pg_lock.rb
296
301
  - app/models/concerns/with_preferences.rb
297
302
  - app/models/concerns/with_profile.rb
@@ -336,6 +341,13 @@ files:
336
341
  - app/models/message.rb
337
342
  - app/models/notification.rb
338
343
  - app/models/organization.rb
344
+ - app/models/organization_access_mode.rb
345
+ - app/models/organization_access_mode/base.rb
346
+ - app/models/organization_access_mode/coming_soon.rb
347
+ - app/models/organization_access_mode/forbidden.rb
348
+ - app/models/organization_access_mode/full.rb
349
+ - app/models/organization_access_mode/gone.rb
350
+ - app/models/organization_access_mode/read_only.rb
339
351
  - app/models/preferences.rb
340
352
  - app/models/progress.rb
341
353
  - app/models/stats.rb