mumuki-domain 7.10.0 → 7.12.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +6 -0
- data/app/models/application_record.rb +10 -0
- data/app/models/assignment.rb +17 -2
- data/app/models/avatar.rb +2 -0
- data/app/models/concerns/awardee.rb +27 -0
- data/app/models/concerns/contextualization.rb +6 -2
- data/app/models/concerns/with_description.rb +2 -5
- data/app/models/concerns/with_medal.rb +7 -0
- data/app/models/concerns/with_progress.rb +4 -0
- data/app/models/concerns/with_terms_acceptance.rb +65 -0
- data/app/models/content.rb +3 -2
- data/app/models/discussion.rb +8 -1
- data/app/models/guide.rb +4 -1
- data/app/models/indicator.rb +14 -1
- data/app/models/medal.rb +3 -0
- data/app/models/organization.rb +51 -4
- data/app/models/term.rb +40 -0
- data/app/models/topic.rb +4 -1
- data/app/models/usage.rb +5 -0
- data/app/models/user.rb +17 -10
- data/db/migrate/20200828152812_create_medals.rb +8 -0
- data/db/migrate/20200828162829_add_medal_to_content.rb +7 -0
- data/db/migrate/20200915123020_add_polymorphic_avatars_to_users.rb +6 -0
- data/db/migrate/20200915131621_add_once_completed_to_indicators.rb +5 -0
- data/db/migrate/20201007143455_add_terms_for_user.rb +14 -0
- data/db/migrate/20201009193949_add_status_updated_fields_to_discussion.rb +6 -0
- data/db/migrate/20201019191036_add_misplaced_flag_to_submission.rb +5 -0
- data/db/migrate/20201026222942_add_display_name_and_description_to_organizations.rb +6 -0
- data/db/migrate/20201026225312_add_organization_wins_page_flag.rb +5 -0
- data/db/migrate/20201027134205_add_immersible_to_organization.rb +5 -0
- data/db/migrate/20201027152806_create_terms.rb +12 -0
- data/lib/mumuki/domain/extensions/array.rb +13 -0
- data/lib/mumuki/domain/factories.rb +3 -1
- data/lib/mumuki/domain/factories/medal_factory.rb +6 -0
- data/lib/mumuki/domain/factories/organization_factory.rb +1 -0
- data/lib/mumuki/domain/factories/term_factory.rb +7 -0
- data/lib/mumuki/domain/helpers/organization.rb +4 -0
- data/lib/mumuki/domain/helpers/user.rb +18 -1
- data/lib/mumuki/domain/incognito.rb +27 -0
- data/lib/mumuki/domain/locales/activerecord/en.yml +2 -0
- data/lib/mumuki/domain/locales/activerecord/es.yml +2 -0
- data/lib/mumuki/domain/locales/defaults/en.yml +4 -0
- data/lib/mumuki/domain/locales/defaults/es-CL.yml +4 -0
- data/lib/mumuki/domain/locales/defaults/es.yml +4 -0
- data/lib/mumuki/domain/locales/defaults/pt.yml +4 -0
- data/lib/mumuki/domain/submission/base.rb +9 -3
- data/lib/mumuki/domain/submission/solution.rb +4 -0
- data/lib/mumuki/domain/version.rb +1 -1
- metadata +28 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b5cd387b744651b03da4722b106d3c23584a8a610994bf3127fa03cd814e9a64
|
4
|
+
data.tar.gz: 73ecd3f88a806ec887ec68e76554db2468f1b3b0dd9d459197b366bdbd3575fd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d43c7db4b34a5727005cf7f571d7980075fd27fd394942822759bd9e1800626e6880bd1a2bf3085d7332c443d6bdc9602f000e7ee8f3434099c02411a811d8e4
|
7
|
+
data.tar.gz: 24fb849fee899e0636448cedc7c03935931a25902edf5144eb952acb2aabe8d051fb6d6ef9b0c684cde9523b633454e6b6c2c0f24b63c5c54e682f14eef55be2
|
data/README.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
[![Build Status](https://travis-ci.com/mumuki/mumuki-domain.svg?branch=master)](https://travis-ci.com/mumuki/mumuki-domain)
|
2
|
+
[![Code Climate](https://codeclimate.com/github/mumuki/mumuki-domain/badges/gpa.svg)](https://codeclimate.com/github/mumuki/mumuki-domain)
|
3
|
+
[![Test Coverage](https://codeclimate.com/github/mumuki/mumuki-domain/badges/coverage.svg)](https://codeclimate.com/github/mumuki/mumuki-domain)
|
4
|
+
[![Issue Count](https://codeclimate.com/github/mumuki/mumuki-domain/badges/issue_count.svg)](https://codeclimate.com/github/mumuki/mumuki-domain)
|
5
|
+
|
6
|
+
|
1
7
|
# Resource Hashes
|
2
8
|
|
3
9
|
## Runner Hashes
|
@@ -3,6 +3,16 @@ class ApplicationRecord < ActiveRecord::Base
|
|
3
3
|
|
4
4
|
delegate :whitelist_attributes, to: :class
|
5
5
|
|
6
|
+
def self.teaser_on(*args)
|
7
|
+
args.each do |selector|
|
8
|
+
teaser_selector = "#{selector}_teaser"
|
9
|
+
define_method teaser_selector do
|
10
|
+
send(selector)&.markdown_paragraphs&.first
|
11
|
+
end
|
12
|
+
markdown_on teaser_selector, skip_sanitization: true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
6
16
|
def self.defaults(&block)
|
7
17
|
after_initialize :defaults, if: :new_record?
|
8
18
|
define_method :defaults, &block
|
data/app/models/assignment.rb
CHANGED
@@ -91,7 +91,14 @@ class Assignment < Progress
|
|
91
91
|
end
|
92
92
|
|
93
93
|
def notify!
|
94
|
-
|
94
|
+
unless Organization.silenced?
|
95
|
+
update_misplaced!(current_notification_contexts.size > 1)
|
96
|
+
Mumukit::Nuntius.notify! 'submissions', to_resource_h
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def current_notification_contexts
|
101
|
+
[Organization.current, submitter.current_immersive_context_at(exercise)].uniq
|
95
102
|
end
|
96
103
|
|
97
104
|
def notify_to_accessible_organizations!
|
@@ -138,6 +145,10 @@ class Assignment < Progress
|
|
138
145
|
end
|
139
146
|
end
|
140
147
|
|
148
|
+
def manual_evaluation_pending!
|
149
|
+
update! submission_status: :manual_evaluation_pending
|
150
|
+
end
|
151
|
+
|
141
152
|
def passed!
|
142
153
|
update! submission_status: :passed
|
143
154
|
end
|
@@ -169,7 +180,7 @@ class Assignment < Progress
|
|
169
180
|
|
170
181
|
def to_resource_h
|
171
182
|
excluded_fields = %i(created_at exercise_id id organization_id parent_id solution submission_id
|
172
|
-
submission_status submitted_at submitter_id top_submission_status updated_at)
|
183
|
+
submission_status submitted_at submitter_id top_submission_status updated_at misplaced)
|
173
184
|
|
174
185
|
as_json(except: excluded_fields,
|
175
186
|
include: {
|
@@ -238,6 +249,10 @@ class Assignment < Progress
|
|
238
249
|
self.top_submission_status = submission_status unless submission_status.improved_by?(top_submission_status)
|
239
250
|
end
|
240
251
|
|
252
|
+
def update_misplaced!(value)
|
253
|
+
update! misplaced: value if value != misplaced?
|
254
|
+
end
|
255
|
+
|
241
256
|
private
|
242
257
|
|
243
258
|
def update_submissions_count!
|
data/app/models/avatar.rb
CHANGED
@@ -0,0 +1,27 @@
|
|
1
|
+
module Awardee
|
2
|
+
def acquired_medals(organization=Organization.current)
|
3
|
+
@acquired_medals ||= medals_for awarded_contents_in(organization)
|
4
|
+
end
|
5
|
+
|
6
|
+
def unacquired_medals(organization=Organization.current)
|
7
|
+
@unacquired_medals ||= medals_for unawarded_contents_in(organization)
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def medals_for(content)
|
13
|
+
content.map(&:medal)
|
14
|
+
end
|
15
|
+
|
16
|
+
def awarded_contents_in(organization)
|
17
|
+
awardable_contents_in(organization).first
|
18
|
+
end
|
19
|
+
|
20
|
+
def unawarded_contents_in(organization)
|
21
|
+
awardable_contents_in(organization).second
|
22
|
+
end
|
23
|
+
|
24
|
+
def awardable_contents_in(organization)
|
25
|
+
@awardable_contents_in ||= organization.awardable_contents.partition { |c| c.once_completed_for? self, organization }
|
26
|
+
end
|
27
|
+
end
|
@@ -51,7 +51,7 @@ module Contextualization
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def single_visible_test_result?
|
54
|
-
test_results.
|
54
|
+
test_results.single? && visible_success_output?
|
55
55
|
end
|
56
56
|
|
57
57
|
def first_test_result
|
@@ -131,8 +131,12 @@ module Contextualization
|
|
131
131
|
.compact
|
132
132
|
.merge(
|
133
133
|
title: it[:title].affable,
|
134
|
-
result: it[:result]
|
134
|
+
result: it[:result],
|
135
135
|
status: it[:status])
|
136
136
|
end
|
137
137
|
end
|
138
|
+
|
139
|
+
def sanitized_affable_test_results
|
140
|
+
affable_test_results.each { |it| it[:result] = it[:result]&.sanitized }
|
141
|
+
end
|
138
142
|
end
|
@@ -2,11 +2,8 @@ module WithDescription
|
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
|
4
4
|
included do
|
5
|
-
markdown_on :description,
|
5
|
+
markdown_on :description, skip_sanitization: true
|
6
|
+
teaser_on :description
|
6
7
|
validates_presence_of :description
|
7
8
|
end
|
8
|
-
|
9
|
-
def description_teaser
|
10
|
-
description.markdown_paragraphs.first
|
11
|
-
end
|
12
9
|
end
|
@@ -23,6 +23,10 @@ module WithProgress
|
|
23
23
|
progress_for(user, organization).completed?
|
24
24
|
end
|
25
25
|
|
26
|
+
def once_completed_for?(user, organization)
|
27
|
+
progress_for(user, organization).once_completed?
|
28
|
+
end
|
29
|
+
|
26
30
|
private
|
27
31
|
|
28
32
|
def structural_children_changed?(old_structural_children)
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module WithTermsAcceptance
|
2
|
+
def has_forum_terms_to_accept?
|
3
|
+
!has_accepted_all?(forum_terms)
|
4
|
+
end
|
5
|
+
|
6
|
+
def has_profile_terms_to_accept?
|
7
|
+
!has_accepted_all?(profile_terms)
|
8
|
+
end
|
9
|
+
|
10
|
+
def has_role_terms_to_accept?
|
11
|
+
!has_accepted_all?(role_specific_terms)
|
12
|
+
end
|
13
|
+
|
14
|
+
def role_specific_terms
|
15
|
+
@role_specific_terms ||= Term.role_specific_terms_for(self)
|
16
|
+
end
|
17
|
+
|
18
|
+
def forum_terms
|
19
|
+
@forum_terms ||= Term.forum_related_terms
|
20
|
+
end
|
21
|
+
|
22
|
+
def profile_terms
|
23
|
+
@profile_terms ||= Term.profile_terms_for(self)
|
24
|
+
end
|
25
|
+
|
26
|
+
def accept_profile_terms!
|
27
|
+
accept_terms! profile_terms
|
28
|
+
end
|
29
|
+
|
30
|
+
def accept_forum_terms!
|
31
|
+
accept_terms! forum_terms
|
32
|
+
end
|
33
|
+
|
34
|
+
def has_accepted?(term)
|
35
|
+
term_accepted_at_for(term.scope).try { |it| it > term.updated_at }.present?
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def unaccepted_terms_in(terms)
|
41
|
+
terms.reject { |term| has_accepted? term}
|
42
|
+
end
|
43
|
+
|
44
|
+
def unaccepted_terms_scopes_in(terms)
|
45
|
+
unaccepted_terms_in(terms).map(&:scope)
|
46
|
+
end
|
47
|
+
|
48
|
+
def has_accepted_all?(terms)
|
49
|
+
unaccepted_terms_in(terms).blank?
|
50
|
+
end
|
51
|
+
|
52
|
+
def term_accepted_at_for(role)
|
53
|
+
send term_acceptance_field_for(role)
|
54
|
+
end
|
55
|
+
|
56
|
+
def term_acceptance_field_for(role)
|
57
|
+
"#{role}_terms_accepted_at"
|
58
|
+
end
|
59
|
+
|
60
|
+
def accept_terms!(terms)
|
61
|
+
update! unaccepted_terms_scopes_in(terms).to_h { |scope| [term_acceptance_field_for(scope), Time.now] }
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
data/app/models/content.rb
CHANGED
@@ -4,10 +4,11 @@ class Content < ApplicationRecord
|
|
4
4
|
include Mumuki::Domain::Syncable
|
5
5
|
include WithDescription
|
6
6
|
include WithLocale
|
7
|
-
include
|
8
|
-
include WithUsages
|
7
|
+
include WithMedal
|
9
8
|
include WithName
|
10
9
|
include WithProgress
|
10
|
+
include WithSlug
|
11
|
+
include WithUsages
|
11
12
|
|
12
13
|
def to_resource_h(*args)
|
13
14
|
to_expanded_resource_h(*args).compact
|
data/app/models/discussion.rb
CHANGED
@@ -4,7 +4,10 @@ class Discussion < ApplicationRecord
|
|
4
4
|
belongs_to :item, polymorphic: true
|
5
5
|
has_many :messages, -> { order(:created_at) }, dependent: :destroy
|
6
6
|
belongs_to :initiator, class_name: 'User'
|
7
|
+
|
7
8
|
belongs_to :last_moderator_access_by, class_name: 'User', optional: true
|
9
|
+
belongs_to :status_updated_by, class_name: 'User', optional: true
|
10
|
+
|
8
11
|
belongs_to :exercise, foreign_type: :exercise, foreign_key: 'item_id'
|
9
12
|
belongs_to :organization
|
10
13
|
has_many :subscriptions
|
@@ -106,7 +109,11 @@ class Discussion < ApplicationRecord
|
|
106
109
|
end
|
107
110
|
|
108
111
|
def update_status!(status, user)
|
109
|
-
|
112
|
+
if reachable_status_for?(user, status)
|
113
|
+
update! status: status,
|
114
|
+
status_updated_by: user,
|
115
|
+
status_updated_at: Time.now
|
116
|
+
end
|
110
117
|
end
|
111
118
|
|
112
119
|
def has_messages?
|
data/app/models/guide.rb
CHANGED
@@ -51,7 +51,10 @@ class Guide < Content
|
|
51
51
|
joins("left join public.assignments assignments
|
52
52
|
on assignments.exercise_id = exercises.id
|
53
53
|
and assignments.submitter_id = #{user.id}
|
54
|
-
and assignments.submission_status
|
54
|
+
and assignments.submission_status in (
|
55
|
+
#{Mumuki::Domain::Status::Submission::Passed.to_i},
|
56
|
+
#{Mumuki::Domain::Status::Submission::ManualEvaluationPending.to_i}
|
57
|
+
)").
|
55
58
|
where('assignments.id is null')
|
56
59
|
end
|
57
60
|
|
data/app/models/indicator.rb
CHANGED
@@ -37,6 +37,7 @@ class Indicator < Progress
|
|
37
37
|
def clean!
|
38
38
|
self.dirty_by_submission = false
|
39
39
|
self.dirty_by_content_change = false
|
40
|
+
self.once_completed ||= all_children_passed?
|
40
41
|
end
|
41
42
|
|
42
43
|
def refresh_children_count!
|
@@ -54,7 +55,15 @@ class Indicator < Progress
|
|
54
55
|
|
55
56
|
def completed?
|
56
57
|
rebuild!
|
57
|
-
|
58
|
+
all_children_passed?
|
59
|
+
end
|
60
|
+
|
61
|
+
def once_completed?
|
62
|
+
self.once_completed || completed?
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.delete_all_for!(content, organization)
|
66
|
+
where(content: content, organization: organization).delete_all
|
58
67
|
end
|
59
68
|
|
60
69
|
private
|
@@ -74,4 +83,8 @@ class Indicator < Progress
|
|
74
83
|
def parent_content
|
75
84
|
content.usage_in_organization(organization).structural_parent
|
76
85
|
end
|
86
|
+
|
87
|
+
def all_children_passed?
|
88
|
+
children_passed_count == children_count
|
89
|
+
end
|
77
90
|
end
|
data/app/models/medal.rb
ADDED
data/app/models/organization.rb
CHANGED
@@ -11,10 +11,12 @@ class Organization < ApplicationRecord
|
|
11
11
|
serialize :settings, Mumuki::Domain::Organization::Settings
|
12
12
|
serialize :theme, Mumuki::Domain::Organization::Theme
|
13
13
|
|
14
|
-
markdown_on :description
|
14
|
+
markdown_on :description, :display_description, :page_description
|
15
|
+
teaser_on :display_description
|
15
16
|
|
16
17
|
validate :ensure_consistent_public_login
|
17
18
|
validate :ensure_valid_activity_range
|
19
|
+
validate :ensure_not_immersive_and_immersible
|
18
20
|
|
19
21
|
belongs_to :book
|
20
22
|
has_many :usages
|
@@ -97,11 +99,13 @@ class Organization < ApplicationRecord
|
|
97
99
|
end
|
98
100
|
|
99
101
|
def title_suffix
|
100
|
-
|
102
|
+
warn "Don't use title_suffix. Use page_name instead"
|
103
|
+
" - #{page_name}"
|
101
104
|
end
|
102
105
|
|
103
106
|
def site_name
|
104
|
-
|
107
|
+
warn "Don't use site_name. Use display_name instead"
|
108
|
+
name
|
105
109
|
end
|
106
110
|
|
107
111
|
# Tells if the given user can
|
@@ -130,12 +134,55 @@ class Organization < ApplicationRecord
|
|
130
134
|
update! progressive_display_lookahead: lookahead
|
131
135
|
end
|
132
136
|
|
137
|
+
def progressive_display_lookahead=(lookahead)
|
138
|
+
self[:progressive_display_lookahead] = lookahead.to_i.positive? ? lookahead : nil
|
139
|
+
end
|
140
|
+
|
141
|
+
# ==============
|
142
|
+
# Display fields
|
143
|
+
# ==============
|
144
|
+
|
133
145
|
def display_name
|
134
|
-
name.gsub(/\W/, ' ').titleize
|
146
|
+
self[:display_name].presence || name.try { |it| it.gsub(/\W/, ' ').titleize }
|
147
|
+
end
|
148
|
+
|
149
|
+
def display_description
|
150
|
+
self[:display_description].presence || I18n.t('defaults.organization.display_description', name: name)
|
151
|
+
end
|
152
|
+
|
153
|
+
# ===========
|
154
|
+
# Page fields
|
155
|
+
# ===========
|
156
|
+
|
157
|
+
# Since an organization has a single book, both concepts may be merged
|
158
|
+
# when describing a site. In such contexts, wins_page?
|
159
|
+
# control whether the book or the organization header fields are
|
160
|
+
# more important
|
161
|
+
|
162
|
+
def page_name
|
163
|
+
wins_page? ? display_name : book.name
|
164
|
+
end
|
165
|
+
|
166
|
+
def page_description
|
167
|
+
wins_page? ? display_description : book.description
|
168
|
+
end
|
169
|
+
|
170
|
+
def all_contents
|
171
|
+
Usage.where(organization: self)
|
172
|
+
.group_by(&:item_type)
|
173
|
+
.flat_map { |item_type, item| item_type.constantize.where(id: item.map(&:item_id)) }
|
174
|
+
end
|
175
|
+
|
176
|
+
def awardable_contents
|
177
|
+
gamification_enabled? ? all_contents.select(&:medal_id) : []
|
135
178
|
end
|
136
179
|
|
137
180
|
private
|
138
181
|
|
182
|
+
def ensure_not_immersive_and_immersible
|
183
|
+
errors.add(:immersible, :cannot_be_immersive) if immersible? && immersive?
|
184
|
+
end
|
185
|
+
|
139
186
|
def ensure_consistent_public_login
|
140
187
|
errors.add(:base, :consistent_public_login) if settings.customized_login_methods? && public?
|
141
188
|
end
|
data/app/models/term.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
class Term < ApplicationRecord
|
2
|
+
attribute :locale, :string, default: 'es'
|
3
|
+
markdown_on :content
|
4
|
+
|
5
|
+
GENERAL = %w(legal privacy student)
|
6
|
+
ROLE_SPECIFIC = %w(headmaster janitor teacher moderator)
|
7
|
+
FORUM_RELATED = %w(forum)
|
8
|
+
|
9
|
+
validates :locale, uniqueness: { scope: :scope }
|
10
|
+
validates :content, :scope, :header, presence: true
|
11
|
+
|
12
|
+
def self.terms_for(scope, locale)
|
13
|
+
where(scope: scope, locale: locale)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.profile_terms_for(user, locale = I18n.locale)
|
17
|
+
general_terms(locale) + role_specific_terms_for(user, locale)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.role_specific_terms_for(user, locale = I18n.locale)
|
21
|
+
terms_for(current_role_terms_for(user), locale)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.general_terms(locale = I18n.locale)
|
25
|
+
terms_for(GENERAL, locale)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.forum_related_terms(locale = I18n.locale)
|
29
|
+
terms_for(FORUM_RELATED, locale)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.current_role_terms_for(user)
|
33
|
+
return [] unless user.present?
|
34
|
+
(user.any_granted_roles & ROLE_SPECIFIC).to_a
|
35
|
+
end
|
36
|
+
|
37
|
+
def accepted_by?(user)
|
38
|
+
user.term_accepted_at_for(scope).try { |it| it > updated_at }.present?
|
39
|
+
end
|
40
|
+
end
|
data/app/models/topic.rb
CHANGED
@@ -48,7 +48,10 @@ class Topic < Content
|
|
48
48
|
joins("left join public.assignments assignments
|
49
49
|
on assignments.exercise_id = exercises.id
|
50
50
|
and assignments.submitter_id = #{user.id}
|
51
|
-
and assignments.submission_status
|
51
|
+
and assignments.submission_status in (
|
52
|
+
#{Mumuki::Domain::Status::Submission::Passed.to_i},
|
53
|
+
#{Mumuki::Domain::Status::Submission::ManualEvaluationPending.to_i}
|
54
|
+
)").
|
52
55
|
where('assignments.id is null').
|
53
56
|
group('public.guides.id', 'lessons.number').map(&:lesson)
|
54
57
|
end
|
data/app/models/usage.rb
CHANGED
@@ -8,6 +8,7 @@ class Usage < ApplicationRecord
|
|
8
8
|
|
9
9
|
before_save :set_slug
|
10
10
|
before_destroy :destroy_children_usages!
|
11
|
+
before_destroy :delete_associated_indicators!
|
11
12
|
|
12
13
|
def self.destroy_all_where(query)
|
13
14
|
where(query).destroy_all
|
@@ -27,6 +28,10 @@ class Usage < ApplicationRecord
|
|
27
28
|
|
28
29
|
private
|
29
30
|
|
31
|
+
def delete_associated_indicators!
|
32
|
+
Indicator.delete_all_for!(item, organization)
|
33
|
+
end
|
34
|
+
|
30
35
|
def set_slug
|
31
36
|
self.slug = item.slug
|
32
37
|
end
|
data/app/models/user.rb
CHANGED
@@ -4,13 +4,15 @@ class User < ApplicationRecord
|
|
4
4
|
WithUserNavigation,
|
5
5
|
WithReminders,
|
6
6
|
WithDiscussionCreation,
|
7
|
+
Awardee,
|
7
8
|
Disabling,
|
9
|
+
WithTermsAcceptance,
|
8
10
|
Mumuki::Domain::Helpers::User
|
9
11
|
|
10
12
|
serialize :permissions, Mumukit::Auth::Permissions
|
11
13
|
|
12
|
-
has_many :assignments, foreign_key: :submitter_id
|
13
14
|
|
15
|
+
has_many :assignments, foreign_key: :submitter_id
|
14
16
|
has_many :messages, -> { order(created_at: :desc) }, through: :assignments
|
15
17
|
|
16
18
|
has_many :submitted_exercises, through: :assignments, class_name: 'Exercise', source: :exercise
|
@@ -31,11 +33,12 @@ class User < ApplicationRecord
|
|
31
33
|
has_many :exams, through: :exam_authorizations
|
32
34
|
|
33
35
|
enum gender: %i(female male other unspecified)
|
34
|
-
belongs_to :avatar, optional: true
|
36
|
+
belongs_to :avatar, polymorphic: true, optional: true
|
35
37
|
|
36
38
|
before_validation :set_uid!
|
37
39
|
validates :uid, presence: true
|
38
40
|
|
41
|
+
validates :terms_of_service, acceptance: true
|
39
42
|
after_save :welcome_to_new_organizations!, if: :gained_access_to_new_orga?
|
40
43
|
after_initialize :init
|
41
44
|
PLACEHOLDER_IMAGE_URL = 'user_shape.png'.freeze
|
@@ -236,6 +239,18 @@ class User < ApplicationRecord
|
|
236
239
|
end
|
237
240
|
end
|
238
241
|
|
242
|
+
def current_organic_context
|
243
|
+
Organization.current? ? Organization.current : main_organization
|
244
|
+
end
|
245
|
+
|
246
|
+
def current_immersive_context_at(exercise)
|
247
|
+
if Organization.current?
|
248
|
+
immersive_organization_at(exercise) || Organization.current
|
249
|
+
else
|
250
|
+
main_organization
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
239
254
|
private
|
240
255
|
|
241
256
|
def welcome_to_new_organizations!
|
@@ -289,12 +304,4 @@ class User < ApplicationRecord
|
|
289
304
|
def self.buried_profile
|
290
305
|
(@buried_profile || {}).slice(:first_name, :last_name, :email)
|
291
306
|
end
|
292
|
-
|
293
|
-
def current_organic_context
|
294
|
-
if Organization.current?
|
295
|
-
Organization.current
|
296
|
-
else
|
297
|
-
main_organization
|
298
|
-
end
|
299
|
-
end
|
300
307
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class AddTermsForUser < ActiveRecord::Migration[5.1]
|
2
|
+
def change
|
3
|
+
add_column :users, :headmaster_terms_accepted_at, :datetime
|
4
|
+
add_column :users, :janitor_terms_accepted_at, :datetime
|
5
|
+
add_column :users, :moderator_terms_accepted_at, :datetime
|
6
|
+
add_column :users, :student_terms_accepted_at, :datetime
|
7
|
+
add_column :users, :teacher_terms_accepted_at, :datetime
|
8
|
+
|
9
|
+
add_column :users, :privacy_terms_accepted_at, :datetime
|
10
|
+
add_column :users, :legal_terms_accepted_at, :datetime
|
11
|
+
|
12
|
+
add_column :users, :forum_terms_accepted_at, :datetime
|
13
|
+
end
|
14
|
+
end
|
@@ -2,6 +2,19 @@ class Array
|
|
2
2
|
def insert_last(element)
|
3
3
|
self + [element]
|
4
4
|
end
|
5
|
+
|
6
|
+
def single
|
7
|
+
first if single?
|
8
|
+
end
|
9
|
+
|
10
|
+
def single!
|
11
|
+
raise 'There is more than one element' unless single?
|
12
|
+
first
|
13
|
+
end
|
14
|
+
|
15
|
+
def single?
|
16
|
+
size == 1
|
17
|
+
end
|
5
18
|
end
|
6
19
|
|
7
20
|
class NilClass
|
@@ -12,8 +12,10 @@ require_relative './factories/guide_factory'
|
|
12
12
|
require_relative './factories/invitation_factory'
|
13
13
|
require_relative './factories/lesson_factory'
|
14
14
|
require_relative './factories/login_settings_factory'
|
15
|
+
require_relative './factories/medal_factory'
|
15
16
|
require_relative './factories/message_factory'
|
16
17
|
require_relative './factories/organization_factory'
|
18
|
+
require_relative './factories/term_factory'
|
17
19
|
require_relative './factories/topic_factory'
|
18
|
-
require_relative './factories/user_factory'
|
19
20
|
require_relative './factories/usage_factory'
|
21
|
+
require_relative './factories/user_factory'
|
@@ -28,6 +28,10 @@ module Mumuki::Domain::Helpers::Organization
|
|
28
28
|
name == 'base'
|
29
29
|
end
|
30
30
|
|
31
|
+
def immersed_in?(other)
|
32
|
+
immersible? && other.immersive? && target_audience == other.target_audience
|
33
|
+
end
|
34
|
+
|
31
35
|
def switch!
|
32
36
|
Mumukit::Platform::Organization.switch! self
|
33
37
|
end
|
@@ -15,6 +15,7 @@ module Mumuki::Domain::Helpers::User
|
|
15
15
|
:protect_permissions_assignment!,
|
16
16
|
:student_granted_organizations,
|
17
17
|
:any_granted_organizations,
|
18
|
+
:any_granted_roles,
|
18
19
|
to: :permissions
|
19
20
|
|
20
21
|
def platform_class_name
|
@@ -87,8 +88,24 @@ module Mumuki::Domain::Helpers::User
|
|
87
88
|
student_granted_organizations.first || any_granted_organizations.first
|
88
89
|
end
|
89
90
|
|
91
|
+
# Deprecated: use `immersive_organization_at` which
|
92
|
+
# properly looks for a single immersive organization taking
|
93
|
+
# current organization and path into account
|
90
94
|
def has_immersive_main_organization?
|
91
|
-
|
95
|
+
main_organization.try(&:immersive?).present?
|
96
|
+
end
|
97
|
+
|
98
|
+
def immersive_organization_at(path_item, current = Organization.current)
|
99
|
+
immersive_organizations_at(path_item, current).single
|
100
|
+
end
|
101
|
+
|
102
|
+
def immersive_organizations_at(path_item, current = Organization.current)
|
103
|
+
return [] unless current.immersible?
|
104
|
+
|
105
|
+
usage_filter = path_item ? lambda { |it| path_item.used_in?(it) } : lambda { |_| true }
|
106
|
+
student_granted_organizations
|
107
|
+
.select { |it| current.immersed_in?(it) }
|
108
|
+
.select(&usage_filter)
|
92
109
|
end
|
93
110
|
|
94
111
|
## API Exposure
|
@@ -48,6 +48,33 @@ module Mumuki::Domain
|
|
48
48
|
false
|
49
49
|
end
|
50
50
|
|
51
|
+
def current_immersive_context_at(_)
|
52
|
+
nil
|
53
|
+
end
|
54
|
+
|
55
|
+
def immersive_organizations_at(_)
|
56
|
+
[]
|
57
|
+
end
|
58
|
+
|
59
|
+
def any_granted_roles
|
60
|
+
[]
|
61
|
+
end
|
62
|
+
|
63
|
+
# ========
|
64
|
+
# Terms
|
65
|
+
# ========
|
66
|
+
|
67
|
+
# It avoids role terms acceptance redirections
|
68
|
+
def has_role_terms_to_accept?
|
69
|
+
false
|
70
|
+
end
|
71
|
+
|
72
|
+
# It makes terms UI to be shown as if no terms were accepted
|
73
|
+
# It does not force any term to be accepted though
|
74
|
+
def has_accepted?(term)
|
75
|
+
false
|
76
|
+
end
|
77
|
+
|
51
78
|
# ========
|
52
79
|
# Visiting
|
53
80
|
# ========
|
@@ -28,3 +28,5 @@ en:
|
|
28
28
|
base:
|
29
29
|
consistent_public_login: 'A public organization can not restrict login methods'
|
30
30
|
invalid_activity_range: 'The organization activity range are not valid'
|
31
|
+
immersible:
|
32
|
+
cannot_be_immersive: 'An organization cannot be immersible and immersive at the same time'
|
@@ -45,6 +45,8 @@ es:
|
|
45
45
|
base:
|
46
46
|
consistent_public_login: 'Una organización pública no puede restringir los métodos de login'
|
47
47
|
invalid_activity_range: 'La fecha de deshabilitación no puede ser anterior a la de inicio del recorrido'
|
48
|
+
immersible:
|
49
|
+
cannot_be_immersive: 'Una organización no puede ser inmersible e inmersiva a la vez'
|
48
50
|
models:
|
49
51
|
exercise:
|
50
52
|
one: Ejercicio
|
@@ -27,8 +27,10 @@ class Mumuki::Domain::Submission::Base
|
|
27
27
|
results
|
28
28
|
end
|
29
29
|
|
30
|
-
|
31
|
-
|
30
|
+
# By default evaluation strategy is ignored.
|
31
|
+
# Subclasses may override this behaviour
|
32
|
+
def dry_run!(assignment, _evaluation)
|
33
|
+
evaluate! assignment
|
32
34
|
end
|
33
35
|
|
34
36
|
def with_client_result(result)
|
@@ -51,7 +53,11 @@ class Mumuki::Domain::Submission::Base
|
|
51
53
|
|
52
54
|
def save_submission!(assignment)
|
53
55
|
assignment.content = content
|
54
|
-
assignment.organization
|
56
|
+
if assignment.organization != Organization.current
|
57
|
+
assignment.dirty_parent_by_submission! if assignment.organization
|
58
|
+
assignment.organization = Organization.current
|
59
|
+
assignment.parent_id = nil
|
60
|
+
end
|
55
61
|
assignment.save!
|
56
62
|
end
|
57
63
|
|
@@ -1,6 +1,10 @@
|
|
1
1
|
class Mumuki::Domain::Submission::Solution < Mumuki::Domain::Submission::PersistentSubmission
|
2
2
|
attr_accessor :content
|
3
3
|
|
4
|
+
def dry_run!(assignment, evaluation)
|
5
|
+
evaluation.evaluate! assignment, self
|
6
|
+
end
|
7
|
+
|
4
8
|
def try_evaluate!(assignment)
|
5
9
|
assignment
|
6
10
|
.run_tests!({client_result: client_result}.compact.merge(content: content))
|
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: 7.
|
4
|
+
version: 7.12.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: 2020-
|
11
|
+
date: 2020-11-17 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.
|
33
|
+
version: '7.9'
|
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.
|
40
|
+
version: '7.9'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: mumukit-assistant
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,14 +86,14 @@ dependencies:
|
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '1.
|
89
|
+
version: '1.18'
|
90
90
|
type: :runtime
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: '1.
|
96
|
+
version: '1.18'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: mumukit-directives
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -252,6 +252,7 @@ files:
|
|
252
252
|
- app/models/chapter.rb
|
253
253
|
- app/models/complement.rb
|
254
254
|
- app/models/concerns/assistable.rb
|
255
|
+
- app/models/concerns/awardee.rb
|
255
256
|
- app/models/concerns/container.rb
|
256
257
|
- app/models/concerns/contextualization.rb
|
257
258
|
- app/models/concerns/disabling.rb
|
@@ -281,6 +282,7 @@ files:
|
|
281
282
|
- app/models/concerns/with_language.rb
|
282
283
|
- app/models/concerns/with_layout.rb
|
283
284
|
- app/models/concerns/with_locale.rb
|
285
|
+
- app/models/concerns/with_medal.rb
|
284
286
|
- app/models/concerns/with_messages.rb
|
285
287
|
- app/models/concerns/with_name.rb
|
286
288
|
- app/models/concerns/with_number.rb
|
@@ -294,6 +296,7 @@ files:
|
|
294
296
|
- app/models/concerns/with_scoped_queries/sort.rb
|
295
297
|
- app/models/concerns/with_slug.rb
|
296
298
|
- app/models/concerns/with_target_audience.rb
|
299
|
+
- app/models/concerns/with_terms_acceptance.rb
|
297
300
|
- app/models/concerns/with_usages.rb
|
298
301
|
- app/models/concerns/with_user_navigation.rb
|
299
302
|
- app/models/content.rb
|
@@ -315,11 +318,13 @@ files:
|
|
315
318
|
- app/models/invitation.rb
|
316
319
|
- app/models/language.rb
|
317
320
|
- app/models/lesson.rb
|
321
|
+
- app/models/medal.rb
|
318
322
|
- app/models/message.rb
|
319
323
|
- app/models/organization.rb
|
320
324
|
- app/models/progress.rb
|
321
325
|
- app/models/stats.rb
|
322
326
|
- app/models/subscription.rb
|
327
|
+
- app/models/term.rb
|
323
328
|
- app/models/topic.rb
|
324
329
|
- app/models/upvote.rb
|
325
330
|
- app/models/usage.rb
|
@@ -620,6 +625,17 @@ files:
|
|
620
625
|
- db/migrate/20200730221001_add_trusted_for_forum_to_user.rb
|
621
626
|
- db/migrate/20200731081757_add_last_moderator_access_fields_to_discussion.rb
|
622
627
|
- db/migrate/20200804191643_add_incognito_mode_enabled_to_organization.rb
|
628
|
+
- db/migrate/20200828152812_create_medals.rb
|
629
|
+
- db/migrate/20200828162829_add_medal_to_content.rb
|
630
|
+
- db/migrate/20200915123020_add_polymorphic_avatars_to_users.rb
|
631
|
+
- db/migrate/20200915131621_add_once_completed_to_indicators.rb
|
632
|
+
- db/migrate/20201007143455_add_terms_for_user.rb
|
633
|
+
- db/migrate/20201009193949_add_status_updated_fields_to_discussion.rb
|
634
|
+
- db/migrate/20201019191036_add_misplaced_flag_to_submission.rb
|
635
|
+
- db/migrate/20201026222942_add_display_name_and_description_to_organizations.rb
|
636
|
+
- db/migrate/20201026225312_add_organization_wins_page_flag.rb
|
637
|
+
- db/migrate/20201027134205_add_immersible_to_organization.rb
|
638
|
+
- db/migrate/20201027152806_create_terms.rb
|
623
639
|
- lib/mumuki/domain.rb
|
624
640
|
- lib/mumuki/domain/area.rb
|
625
641
|
- lib/mumuki/domain/engine.rb
|
@@ -656,8 +672,10 @@ files:
|
|
656
672
|
- lib/mumuki/domain/factories/invitation_factory.rb
|
657
673
|
- lib/mumuki/domain/factories/lesson_factory.rb
|
658
674
|
- lib/mumuki/domain/factories/login_settings_factory.rb
|
675
|
+
- lib/mumuki/domain/factories/medal_factory.rb
|
659
676
|
- lib/mumuki/domain/factories/message_factory.rb
|
660
677
|
- lib/mumuki/domain/factories/organization_factory.rb
|
678
|
+
- lib/mumuki/domain/factories/term_factory.rb
|
661
679
|
- lib/mumuki/domain/factories/topic_factory.rb
|
662
680
|
- lib/mumuki/domain/factories/usage_factory.rb
|
663
681
|
- lib/mumuki/domain/factories/user_factory.rb
|
@@ -675,6 +693,10 @@ files:
|
|
675
693
|
- lib/mumuki/domain/locales/console_submission/es-CL.yml
|
676
694
|
- lib/mumuki/domain/locales/console_submission/es.yml
|
677
695
|
- lib/mumuki/domain/locales/console_submission/pt.yml
|
696
|
+
- lib/mumuki/domain/locales/defaults/en.yml
|
697
|
+
- lib/mumuki/domain/locales/defaults/es-CL.yml
|
698
|
+
- lib/mumuki/domain/locales/defaults/es.yml
|
699
|
+
- lib/mumuki/domain/locales/defaults/pt.yml
|
678
700
|
- lib/mumuki/domain/organization.rb
|
679
701
|
- lib/mumuki/domain/organization/profile.rb
|
680
702
|
- lib/mumuki/domain/organization/settings.rb
|