mumuki-domain 8.2.0 → 8.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +2 -0
- data/app/models/application_record.rb +6 -0
- data/app/models/assignment.rb +4 -0
- data/app/models/book.rb +1 -1
- data/app/models/chapter.rb +3 -0
- data/app/models/concerns/guide_container.rb +2 -1
- data/app/models/concerns/with_assignments.rb +1 -0
- data/app/models/concerns/with_assignments_batch.rb +31 -0
- data/app/models/concerns/with_notifications.rb +17 -0
- data/app/models/concerns/with_preferences.rb +7 -0
- data/app/models/concerns/with_timed_enablement.rb +11 -0
- data/app/models/discussion.rb +4 -0
- data/app/models/exam.rb +4 -8
- data/app/models/exam_authorization_request.rb +26 -0
- data/app/models/exam_registration.rb +46 -0
- data/app/models/exam_registration/authorization_criterion.rb +61 -0
- data/app/models/exercise.rb +5 -0
- data/app/models/guide.rb +7 -2
- data/app/models/message.rb +4 -0
- data/app/models/notification.rb +9 -0
- data/app/models/organization.rb +5 -1
- data/app/models/preferences.rb +17 -0
- data/app/models/stats.rb +4 -0
- data/app/models/topic.rb +16 -7
- data/app/models/user.rb +8 -5
- data/app/models/user_stats.rb +32 -0
- data/app/models/with_stats.rb +2 -1
- data/db/migrate/20210111125810_add_uppercase_mode_to_user.rb +5 -0
- data/db/migrate/20210114200545_create_exam_registrations.rb +14 -0
- data/db/migrate/20210118180941_create_exam_authorization_request.rb +13 -0
- data/db/migrate/20210118194904_create_notification.rb +13 -0
- data/db/migrate/20210119160440_add_prevent_manual_evaluation_content_to_organizations.rb +5 -0
- data/db/migrate/20210119190204_create_exam_registration_exam_join_table.rb +8 -0
- data/lib/mumuki/domain/factories.rb +3 -0
- data/lib/mumuki/domain/factories/book_factory.rb +13 -0
- data/lib/mumuki/domain/factories/exam_authorization_request_factory.rb +6 -0
- data/lib/mumuki/domain/factories/exam_registration_factory.rb +8 -0
- data/lib/mumuki/domain/factories/notification_factory.rb +5 -0
- data/lib/mumuki/domain/helpers/organization.rb +4 -0
- data/lib/mumuki/domain/incognito.rb +4 -1
- data/lib/mumuki/domain/version.rb +1 -1
- metadata +24 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 339d512e2040015c6556f9671fd00281edf9b651a5dc9db8f860d7607989cd65
|
4
|
+
data.tar.gz: a03fe1fe26593986af16d89c1df6cc6e9b8f21c3e9e240de41f884482e75c6d7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ea2475a456f0558a61e798d27e88921a7a69b134dc38431c1244033d778dbc1b91ce4a974acaa6abbf77cceed03134430cb378f2e4d4561f39d297a7040a9745
|
7
|
+
data.tar.gz: 1321dbd879300fb21e87ef044bae0a7bcbca62ff9fc27cf55bfac1e02cc122273952add367a94381a54e1b730e68549659e5abb9762795071e8c7fa89d6a3699
|
data/Rakefile
CHANGED
@@ -129,6 +129,12 @@ class ApplicationRecord < ActiveRecord::Base
|
|
129
129
|
end
|
130
130
|
end
|
131
131
|
|
132
|
+
def self.enum_prefixed_translations_for(selector)
|
133
|
+
send(selector.to_s.pluralize).map do |key, _|
|
134
|
+
[I18n.t("#{selector}_#{key}", default: key.to_sym), key]
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
132
138
|
private
|
133
139
|
|
134
140
|
def raise_foreign_key_error!
|
data/app/models/assignment.rb
CHANGED
@@ -263,6 +263,10 @@ class Assignment < Progress
|
|
263
263
|
update! misplaced: value if value != misplaced?
|
264
264
|
end
|
265
265
|
|
266
|
+
def self.build_for(user, exercise, organization)
|
267
|
+
Assignment.new submitter: user, exercise: exercise, organization: organization
|
268
|
+
end
|
269
|
+
|
266
270
|
private
|
267
271
|
|
268
272
|
def duplicates_key
|
data/app/models/book.rb
CHANGED
@@ -18,7 +18,7 @@ class Book < Content
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def discussions_in_organization(organization = Organization.current)
|
21
|
-
Discussion.where(organization: organization).includes(exercise: [:language, :guide])
|
21
|
+
Discussion.where(organization: organization, item: organization.exercises).includes(exercise: [:language, :guide])
|
22
22
|
end
|
23
23
|
|
24
24
|
def first_chapter
|
data/app/models/chapter.rb
CHANGED
@@ -13,6 +13,9 @@ class Chapter < ApplicationRecord
|
|
13
13
|
|
14
14
|
has_many :exercises, through: :topic
|
15
15
|
|
16
|
+
delegate :monolesson?, :monolesson, :first_lesson, to: :topic
|
17
|
+
|
18
|
+
delegate :next_exercise, :stats_for, to: :monolesson, allow_nil: true
|
16
19
|
|
17
20
|
def used_in?(organization)
|
18
21
|
organization.book == self.book
|
@@ -19,6 +19,7 @@ module WithAssignments
|
|
19
19
|
end
|
20
20
|
|
21
21
|
# TODO: When the organization is used in this one, please change guide.pending_exercises
|
22
|
+
# TODO: Please do the same on WithAssignmentsBatch
|
22
23
|
def find_assignment_for(user, _organization)
|
23
24
|
assignments.find_by(submitter: user)
|
24
25
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# WithAssignmentsBatch mirrors the WithAssignment mixin
|
2
|
+
# but implements operations in batches, so that they outperform
|
3
|
+
# their counterparts
|
4
|
+
module WithAssignmentsBatch
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
def find_assignments_for(user, _organization = Organization.current, &block)
|
8
|
+
block = block_given? ? block : lambda { |it, _e| it }
|
9
|
+
|
10
|
+
return exercises.map { |it| block.call nil, it } unless user
|
11
|
+
|
12
|
+
pairs = exercises.map { |it| [it.id, [nil, it]] }.to_h
|
13
|
+
Assignment.where(submitter: user, exercise: exercises).each do |it|
|
14
|
+
pairs[it.exercise_id][0] = it
|
15
|
+
end
|
16
|
+
|
17
|
+
pairs.values.map { |assignment, exercise| block.call assignment, exercise }
|
18
|
+
end
|
19
|
+
|
20
|
+
def statuses_for(user, organization = Organization.current)
|
21
|
+
find_assignments_for user, organization do |it|
|
22
|
+
it&.status || Mumuki::Domain::Status::Submission::Pending
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def assignments_for(user, organization = Organization.current)
|
27
|
+
find_assignments_for user, organization do |it, exercise|
|
28
|
+
it || Assignment.build_for(user, exercise, organization)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module WithNotifications
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
def unread_messages
|
5
|
+
messages.where read: false
|
6
|
+
end
|
7
|
+
|
8
|
+
def unread_notifications
|
9
|
+
# TODO: message and discussion should trigger a notification instead of being one
|
10
|
+
all = notifications.where(read: false) + unread_messages + unread_discussions
|
11
|
+
all.sort_by(&:created_at).reverse
|
12
|
+
end
|
13
|
+
|
14
|
+
def read_notification!(target)
|
15
|
+
notifications.find_by(target: target)&.mark_as_read!
|
16
|
+
end
|
17
|
+
end
|
data/app/models/discussion.rb
CHANGED
data/app/models/exam.rb
CHANGED
@@ -3,13 +3,17 @@ class Exam < ApplicationRecord
|
|
3
3
|
include GuideContainer
|
4
4
|
include FriendlyName
|
5
5
|
include TerminalNavigation
|
6
|
+
include WithTimedEnablement
|
6
7
|
|
7
8
|
belongs_to :organization
|
8
9
|
belongs_to :course
|
9
10
|
|
10
11
|
has_many :authorizations, class_name: 'ExamAuthorization', dependent: :destroy
|
12
|
+
has_many :authorization_requests, class_name: 'ExamAuthorizationRequest', dependent: :destroy
|
11
13
|
has_many :users, through: :authorizations
|
12
14
|
|
15
|
+
has_and_belongs_to_many :exam_registrations
|
16
|
+
|
13
17
|
enum passing_criterion_type: [:none, :percentage, :passed_exercises], _prefix: :passing_criterion
|
14
18
|
|
15
19
|
validates_presence_of :start_time, :end_time
|
@@ -27,14 +31,6 @@ class Exam < ApplicationRecord
|
|
27
31
|
organization == self.organization
|
28
32
|
end
|
29
33
|
|
30
|
-
def enabled?
|
31
|
-
enabled_range.cover? DateTime.current
|
32
|
-
end
|
33
|
-
|
34
|
-
def enabled_range
|
35
|
-
start_time..end_time
|
36
|
-
end
|
37
|
-
|
38
34
|
def enabled_for?(user)
|
39
35
|
enabled_range_for(user).cover? DateTime.current
|
40
36
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class ExamAuthorizationRequest < ApplicationRecord
|
2
|
+
include TerminalNavigation
|
3
|
+
|
4
|
+
belongs_to :exam
|
5
|
+
belongs_to :user
|
6
|
+
belongs_to :organization
|
7
|
+
belongs_to :exam_registration
|
8
|
+
|
9
|
+
enum status: %i(pending approved rejected)
|
10
|
+
|
11
|
+
after_update :notify_user!
|
12
|
+
|
13
|
+
def try_authorize!
|
14
|
+
exam.authorize! user if approved?
|
15
|
+
end
|
16
|
+
|
17
|
+
def name
|
18
|
+
exam_registration.description
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def notify_user!
|
24
|
+
Notification.create! organization: organization, user: user, target: self if saved_change_to_status?
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
class ExamRegistration < ApplicationRecord
|
2
|
+
include WithTimedEnablement
|
3
|
+
include TerminalNavigation
|
4
|
+
|
5
|
+
belongs_to :organization
|
6
|
+
has_and_belongs_to_many :exams
|
7
|
+
has_many :authorization_requests, class_name: 'ExamAuthorizationRequest'
|
8
|
+
|
9
|
+
enum authorization_criterion_type: %i(none passed_exercises), _prefix: :authorization_criterion
|
10
|
+
|
11
|
+
before_save :ensure_valid_authorization_criterion!
|
12
|
+
|
13
|
+
delegate :meets_authorization_criteria?, :process_request!, to: :authorization_criterion
|
14
|
+
|
15
|
+
alias_attribute :name, :description
|
16
|
+
|
17
|
+
def authorization_criterion
|
18
|
+
@authorization_criterion ||= ExamRegistration::AuthorizationCriterion.parse(authorization_criterion_type, authorization_criterion_value)
|
19
|
+
end
|
20
|
+
|
21
|
+
def ensure_valid_authorization_criterion!
|
22
|
+
authorization_criterion.ensure_valid!
|
23
|
+
end
|
24
|
+
|
25
|
+
def start!(users)
|
26
|
+
users.each &method(:notify_user!)
|
27
|
+
end
|
28
|
+
|
29
|
+
def process_requests!
|
30
|
+
authorization_requests.each do |it|
|
31
|
+
process_request! it
|
32
|
+
it.try_authorize!
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def authorization_request_for(user)
|
37
|
+
authorization_requests.find_by(user: user) ||
|
38
|
+
ExamAuthorizationRequest.new(exam_registration: self, organization: organization)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def notify_user!(user)
|
44
|
+
Notification.create! organization: organization, user: user, target: self
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
class ExamRegistration::AuthorizationCriterion
|
2
|
+
attr_reader :value
|
3
|
+
|
4
|
+
def initialize(value)
|
5
|
+
@value = value
|
6
|
+
end
|
7
|
+
|
8
|
+
def type
|
9
|
+
self.class.name.demodulize.underscore
|
10
|
+
end
|
11
|
+
|
12
|
+
def as_json
|
13
|
+
{ type: type, value: value }
|
14
|
+
end
|
15
|
+
|
16
|
+
def ensure_valid!
|
17
|
+
raise "Invalid criterion value #{value} for #{type}" unless valid?
|
18
|
+
end
|
19
|
+
|
20
|
+
def process_request!(authorization_request)
|
21
|
+
authorization_request.update! status: authorization_status_for(authorization_request)
|
22
|
+
end
|
23
|
+
|
24
|
+
def authorization_status_for(authorization_request)
|
25
|
+
meets_authorization_criteria?(authorization_request) ? :approved : :rejected
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.parse(type, value)
|
29
|
+
parse_criterion_type(type, value)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.parse_criterion_type(type, value)
|
33
|
+
"ExamRegistration::AuthorizationCriterion::#{type.camelize}".constantize.new(value)
|
34
|
+
rescue
|
35
|
+
raise "Invalid criterion type #{type}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class ExamRegistration::AuthorizationCriterion::None < ExamRegistration::AuthorizationCriterion
|
40
|
+
def initialize(_)
|
41
|
+
@value = nil
|
42
|
+
end
|
43
|
+
|
44
|
+
def valid?
|
45
|
+
!value
|
46
|
+
end
|
47
|
+
|
48
|
+
def meets_authorization_criteria?(_authorization_request)
|
49
|
+
true
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class ExamRegistration::AuthorizationCriterion::PassedExercises < ExamRegistration::AuthorizationCriterion
|
54
|
+
def valid?
|
55
|
+
value.positive?
|
56
|
+
end
|
57
|
+
|
58
|
+
def meets_authorization_criteria?(authorization_request)
|
59
|
+
authorization_request.user.passed_submissions_count_in(authorization_request.organization) >= value
|
60
|
+
end
|
61
|
+
end
|
data/app/models/exercise.rb
CHANGED
@@ -28,6 +28,10 @@ class Exercise < ApplicationRecord
|
|
28
28
|
|
29
29
|
defaults { self.submissions_count = 0 }
|
30
30
|
|
31
|
+
def self.default_scope
|
32
|
+
where(manual_evaluation: false) if Organization.safe_current&.prevent_manual_evaluation_content
|
33
|
+
end
|
34
|
+
|
31
35
|
alias_method :progress_for, :assignment_for
|
32
36
|
|
33
37
|
serialize :choices, Array
|
@@ -38,6 +42,7 @@ class Exercise < ApplicationRecord
|
|
38
42
|
|
39
43
|
randomize(*RANDOMIZED_FIELDS)
|
40
44
|
delegate :timed?, to: :navigable_parent
|
45
|
+
delegate :stats_for, to: :guide
|
41
46
|
|
42
47
|
def console?
|
43
48
|
queriable?
|
data/app/models/guide.rb
CHANGED
@@ -6,7 +6,8 @@ class Guide < Content
|
|
6
6
|
|
7
7
|
include WithStats,
|
8
8
|
WithExpectations,
|
9
|
-
WithLanguage
|
9
|
+
WithLanguage,
|
10
|
+
WithAssignmentsBatch
|
10
11
|
|
11
12
|
markdown_on :corollary, :sources, :learn_more, :teacher_info
|
12
13
|
|
@@ -42,7 +43,11 @@ class Guide < Content
|
|
42
43
|
end
|
43
44
|
|
44
45
|
def next_exercise(user)
|
45
|
-
user.
|
46
|
+
if user.present?
|
47
|
+
user.next_exercise_at(self)
|
48
|
+
else
|
49
|
+
first_exercise
|
50
|
+
end
|
46
51
|
end
|
47
52
|
|
48
53
|
# TODO: Make use of pending_siblings logic
|
data/app/models/message.rb
CHANGED
data/app/models/organization.rb
CHANGED
@@ -138,6 +138,10 @@ class Organization < ApplicationRecord
|
|
138
138
|
self[:progressive_display_lookahead] = lookahead.to_i.positive? ? lookahead : nil
|
139
139
|
end
|
140
140
|
|
141
|
+
def activity_start_date(default_date)
|
142
|
+
[default_date, in_preparation_until&.to_date].compact.max
|
143
|
+
end
|
144
|
+
|
141
145
|
# ==============
|
142
146
|
# Display fields
|
143
147
|
# ==============
|
@@ -207,7 +211,7 @@ class Organization < ApplicationRecord
|
|
207
211
|
end
|
208
212
|
|
209
213
|
def silenced?
|
210
|
-
!
|
214
|
+
!current? || current.silent?
|
211
215
|
end
|
212
216
|
|
213
217
|
def sync_key_id_field
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Preferences
|
2
|
+
include ActiveModel::Model
|
3
|
+
|
4
|
+
def self.attributes
|
5
|
+
[:uppercase_mode]
|
6
|
+
end
|
7
|
+
|
8
|
+
attr_accessor *self.attributes
|
9
|
+
|
10
|
+
def self.from_attributes(*args)
|
11
|
+
new self.attributes.zip(args).to_h
|
12
|
+
end
|
13
|
+
|
14
|
+
def uppercase?
|
15
|
+
uppercase_mode
|
16
|
+
end
|
17
|
+
end
|
data/app/models/stats.rb
CHANGED
data/app/models/topic.rb
CHANGED
@@ -35,6 +35,14 @@ class Topic < Content
|
|
35
35
|
Chapter.where(topic: self).map(&:book).each(&:reindex_usages!)
|
36
36
|
end
|
37
37
|
|
38
|
+
def monolesson
|
39
|
+
@monolesson ||= lessons.to_a.single
|
40
|
+
end
|
41
|
+
|
42
|
+
def monolesson?
|
43
|
+
monolesson.present?
|
44
|
+
end
|
45
|
+
|
38
46
|
## Forking
|
39
47
|
|
40
48
|
def fork_children_into!(dup, organization, syncer)
|
@@ -42,18 +50,19 @@ class Topic < Content
|
|
42
50
|
end
|
43
51
|
|
44
52
|
def pending_lessons(user)
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
joins(
|
53
|
+
lessons
|
54
|
+
.includes(:guide)
|
55
|
+
.references(:guide)
|
56
|
+
.joins('left join exercises exercises on exercises.guide_id = guides.id')
|
57
|
+
.joins("left join assignments assignments
|
49
58
|
on assignments.exercise_id = exercises.id
|
50
59
|
and assignments.submitter_id = #{user.id}
|
51
60
|
and assignments.submission_status in (
|
52
61
|
#{Mumuki::Domain::Status::Submission::Passed.to_i},
|
53
62
|
#{Mumuki::Domain::Status::Submission::ManualEvaluationPending.to_i}
|
54
|
-
)")
|
55
|
-
where('assignments.id is null')
|
56
|
-
group('
|
63
|
+
)")
|
64
|
+
.where('assignments.id is null')
|
65
|
+
.group('guides.id', 'lessons.number', 'lessons.id')
|
57
66
|
end
|
58
67
|
|
59
68
|
private
|
data/app/models/user.rb
CHANGED
@@ -3,15 +3,18 @@ class User < ApplicationRecord
|
|
3
3
|
include WithProfile,
|
4
4
|
WithUserNavigation,
|
5
5
|
WithReminders,
|
6
|
+
WithNotifications,
|
6
7
|
WithDiscussionCreation,
|
7
8
|
Awardee,
|
8
9
|
Disabling,
|
9
10
|
WithTermsAcceptance,
|
11
|
+
WithPreferences,
|
10
12
|
Mumuki::Domain::Helpers::User
|
11
13
|
|
12
14
|
serialize :permissions, Mumukit::Auth::Permissions
|
13
15
|
|
14
16
|
|
17
|
+
has_many :notifications
|
15
18
|
has_many :assignments, foreign_key: :submitter_id
|
16
19
|
has_many :messages, -> { order(created_at: :desc) }, through: :assignments
|
17
20
|
|
@@ -49,6 +52,10 @@ class User < ApplicationRecord
|
|
49
52
|
last_guide.try(:lesson)
|
50
53
|
end
|
51
54
|
|
55
|
+
def passed_submissions_count_in(organization)
|
56
|
+
assignments.where(top_submission_status: Mumuki::Domain::Status::Submission::Passed.to_i, organization: organization).count
|
57
|
+
end
|
58
|
+
|
52
59
|
def submissions_count
|
53
60
|
assignments.pluck(:submissions_count).sum
|
54
61
|
end
|
@@ -69,10 +76,6 @@ class User < ApplicationRecord
|
|
69
76
|
assignments.where(status: Mumuki::Domain::Status::Submission::Passed.to_i)
|
70
77
|
end
|
71
78
|
|
72
|
-
def unread_messages
|
73
|
-
messages.where read: false
|
74
|
-
end
|
75
|
-
|
76
79
|
def visit!(organization)
|
77
80
|
update!(last_organization: organization) if organization != last_organization
|
78
81
|
end
|
@@ -206,7 +209,7 @@ class User < ApplicationRecord
|
|
206
209
|
end
|
207
210
|
|
208
211
|
def build_assignment(exercise, organization)
|
209
|
-
|
212
|
+
Assignment.build_for(self, exercise, organization)
|
210
213
|
end
|
211
214
|
|
212
215
|
def pending_siblings_at(content)
|
data/app/models/user_stats.rb
CHANGED
@@ -10,7 +10,39 @@ class UserStats < ApplicationRecord
|
|
10
10
|
self.stats_for(user).exp
|
11
11
|
end
|
12
12
|
|
13
|
+
def activity(date_range = nil)
|
14
|
+
date_filter = { submitted_at: date_range }.compact
|
15
|
+
{
|
16
|
+
exercises: {
|
17
|
+
solved_count: organization_exercises
|
18
|
+
.joins(:assignments)
|
19
|
+
.where(assignments: { top_submission_status: [:passed, :skipped], submitter: user }.merge(date_filter))
|
20
|
+
.count,
|
21
|
+
count: organization_exercises.count},
|
22
|
+
|
23
|
+
messages: messages_in_discussions_count(date_range)
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
13
27
|
def add_exp!(points)
|
14
28
|
self.exp += points
|
15
29
|
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def messages_in_discussions_count(date_range = nil)
|
34
|
+
date_filter = { date: date_range }.compact
|
35
|
+
result = Message.joins(:discussion)
|
36
|
+
.where({sender: user.uid, discussions: { organization: organization }}.merge(date_filter))
|
37
|
+
.group(:approved)
|
38
|
+
.count
|
39
|
+
unapproved = result[false] || 0
|
40
|
+
approved = result[true] || 0
|
41
|
+
|
42
|
+
{ count: unapproved + approved, approved: approved }
|
43
|
+
end
|
44
|
+
|
45
|
+
def organization_exercises
|
46
|
+
@organization_exercises ||= organization.exercises
|
47
|
+
end
|
16
48
|
end
|
data/app/models/with_stats.rb
CHANGED
@@ -0,0 +1,14 @@
|
|
1
|
+
class CreateExamRegistrations < ActiveRecord::Migration[5.1]
|
2
|
+
def change
|
3
|
+
create_table :exam_registrations do |t|
|
4
|
+
t.string :description
|
5
|
+
t.datetime :start_time, null: false
|
6
|
+
t.datetime :end_time, null: false
|
7
|
+
t.integer :authorization_criterion_type, default: 0
|
8
|
+
t.integer :authorization_criterion_value
|
9
|
+
t.references :organization, index: true
|
10
|
+
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class CreateExamAuthorizationRequest < ActiveRecord::Migration[5.1]
|
2
|
+
def change
|
3
|
+
create_table :exam_authorization_requests do |t|
|
4
|
+
t.integer :status, default: 0
|
5
|
+
t.references :exam, index: true
|
6
|
+
t.references :exam_registration, index: true
|
7
|
+
t.references :user, index: true
|
8
|
+
t.references :organization, index: true
|
9
|
+
|
10
|
+
t.timestamps
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class CreateNotification < ActiveRecord::Migration[5.1]
|
2
|
+
def change
|
3
|
+
create_table :notifications do |t|
|
4
|
+
t.integer :priority, default: 100
|
5
|
+
t.boolean :read, default: false
|
6
|
+
t.references :target, polymorphic: true
|
7
|
+
t.references :user, index: true
|
8
|
+
t.references :organization, index: true
|
9
|
+
|
10
|
+
t.timestamps
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -7,6 +7,8 @@ require_relative './factories/complement_factory'
|
|
7
7
|
require_relative './factories/course_factory'
|
8
8
|
require_relative './factories/discussion_factory'
|
9
9
|
require_relative './factories/exam_factory'
|
10
|
+
require_relative './factories/exam_authorization_request_factory'
|
11
|
+
require_relative './factories/exam_registration_factory'
|
10
12
|
require_relative './factories/exercise_factory'
|
11
13
|
require_relative './factories/guide_factory'
|
12
14
|
require_relative './factories/invitation_factory'
|
@@ -14,6 +16,7 @@ require_relative './factories/lesson_factory'
|
|
14
16
|
require_relative './factories/login_settings_factory'
|
15
17
|
require_relative './factories/medal_factory'
|
16
18
|
require_relative './factories/message_factory'
|
19
|
+
require_relative './factories/notification_factory'
|
17
20
|
require_relative './factories/organization_factory'
|
18
21
|
require_relative './factories/term_factory'
|
19
22
|
require_relative './factories/topic_factory'
|
@@ -4,4 +4,17 @@ FactoryBot.define do
|
|
4
4
|
description { Faker::Lorem.sentence(word_count: 30) }
|
5
5
|
slug { "mumuki/mumuki-test-book-#{SecureRandom.uuid}" }
|
6
6
|
end
|
7
|
+
|
8
|
+
factory :book_with_full_tree, parent: :book do
|
9
|
+
transient do
|
10
|
+
children_factor { 3 }
|
11
|
+
exercises { create_list(:exercise, children_factor) }
|
12
|
+
lessons { create_list(:lesson, children_factor, exercises: exercises) }
|
13
|
+
chapters { create_list(:chapter, children_factor, lessons: lessons) }
|
14
|
+
end
|
15
|
+
|
16
|
+
after(:build) do |book, evaluator|
|
17
|
+
book.chapters = evaluator.chapters
|
18
|
+
end
|
19
|
+
end
|
7
20
|
end
|
@@ -90,6 +90,9 @@ module Mumuki::Domain
|
|
90
90
|
def visit!(*)
|
91
91
|
end
|
92
92
|
|
93
|
+
def currently_in_exam?
|
94
|
+
false
|
95
|
+
end
|
93
96
|
# ========
|
94
97
|
# Progress
|
95
98
|
# ========
|
@@ -107,7 +110,7 @@ module Mumuki::Domain
|
|
107
110
|
end
|
108
111
|
|
109
112
|
def build_assignment(exercise, organization)
|
110
|
-
Assignment.
|
113
|
+
Assignment.build_for(self, exercise, organization)
|
111
114
|
end
|
112
115
|
|
113
116
|
def pending_siblings_at(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: 8.
|
4
|
+
version: 8.6.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-
|
11
|
+
date: 2021-02-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -170,28 +170,28 @@ dependencies:
|
|
170
170
|
requirements:
|
171
171
|
- - "~>"
|
172
172
|
- !ruby/object:Gem::Version
|
173
|
-
version: '
|
173
|
+
version: '6.0'
|
174
174
|
type: :runtime
|
175
175
|
prerelease: false
|
176
176
|
version_requirements: !ruby/object:Gem::Requirement
|
177
177
|
requirements:
|
178
178
|
- - "~>"
|
179
179
|
- !ruby/object:Gem::Version
|
180
|
-
version: '
|
180
|
+
version: '6.0'
|
181
181
|
- !ruby/object:Gem::Dependency
|
182
182
|
name: mumukit-inspection
|
183
183
|
requirement: !ruby/object:Gem::Requirement
|
184
184
|
requirements:
|
185
185
|
- - "~>"
|
186
186
|
- !ruby/object:Gem::Version
|
187
|
-
version: '
|
187
|
+
version: '6.0'
|
188
188
|
type: :runtime
|
189
189
|
prerelease: false
|
190
190
|
version_requirements: !ruby/object:Gem::Requirement
|
191
191
|
requirements:
|
192
192
|
- - "~>"
|
193
193
|
- !ruby/object:Gem::Version
|
194
|
-
version: '
|
194
|
+
version: '6.0'
|
195
195
|
- !ruby/object:Gem::Dependency
|
196
196
|
name: sprockets
|
197
197
|
requirement: !ruby/object:Gem::Requirement
|
@@ -270,6 +270,7 @@ files:
|
|
270
270
|
- app/models/concerns/submittable/triable.rb
|
271
271
|
- app/models/concerns/topic_container.rb
|
272
272
|
- app/models/concerns/with_assignments.rb
|
273
|
+
- app/models/concerns/with_assignments_batch.rb
|
273
274
|
- app/models/concerns/with_case_insensitive_search.rb
|
274
275
|
- app/models/concerns/with_description.rb
|
275
276
|
- app/models/concerns/with_discussion_creation.rb
|
@@ -285,7 +286,9 @@ files:
|
|
285
286
|
- app/models/concerns/with_medal.rb
|
286
287
|
- app/models/concerns/with_messages.rb
|
287
288
|
- app/models/concerns/with_name.rb
|
289
|
+
- app/models/concerns/with_notifications.rb
|
288
290
|
- app/models/concerns/with_number.rb
|
291
|
+
- app/models/concerns/with_preferences.rb
|
289
292
|
- app/models/concerns/with_profile.rb
|
290
293
|
- app/models/concerns/with_progress.rb
|
291
294
|
- app/models/concerns/with_randomizations.rb
|
@@ -297,6 +300,7 @@ files:
|
|
297
300
|
- app/models/concerns/with_slug.rb
|
298
301
|
- app/models/concerns/with_target_audience.rb
|
299
302
|
- app/models/concerns/with_terms_acceptance.rb
|
303
|
+
- app/models/concerns/with_timed_enablement.rb
|
300
304
|
- app/models/concerns/with_usages.rb
|
301
305
|
- app/models/concerns/with_user_navigation.rb
|
302
306
|
- app/models/content.rb
|
@@ -306,6 +310,9 @@ files:
|
|
306
310
|
- app/models/exam.rb
|
307
311
|
- app/models/exam/passing_criterion.rb
|
308
312
|
- app/models/exam_authorization.rb
|
313
|
+
- app/models/exam_authorization_request.rb
|
314
|
+
- app/models/exam_registration.rb
|
315
|
+
- app/models/exam_registration/authorization_criterion.rb
|
309
316
|
- app/models/exercise.rb
|
310
317
|
- app/models/exercise/challenge.rb
|
311
318
|
- app/models/exercise/interactive.rb
|
@@ -320,7 +327,9 @@ files:
|
|
320
327
|
- app/models/lesson.rb
|
321
328
|
- app/models/medal.rb
|
322
329
|
- app/models/message.rb
|
330
|
+
- app/models/notification.rb
|
323
331
|
- app/models/organization.rb
|
332
|
+
- app/models/preferences.rb
|
324
333
|
- app/models/progress.rb
|
325
334
|
- app/models/stats.rb
|
326
335
|
- app/models/subscription.rb
|
@@ -637,6 +646,12 @@ files:
|
|
637
646
|
- db/migrate/20201027134205_add_immersible_to_organization.rb
|
638
647
|
- db/migrate/20201027152806_create_terms.rb
|
639
648
|
- db/migrate/20201130163114_add_banned_from_forum_to_users.rb
|
649
|
+
- db/migrate/20210111125810_add_uppercase_mode_to_user.rb
|
650
|
+
- db/migrate/20210114200545_create_exam_registrations.rb
|
651
|
+
- db/migrate/20210118180941_create_exam_authorization_request.rb
|
652
|
+
- db/migrate/20210118194904_create_notification.rb
|
653
|
+
- db/migrate/20210119160440_add_prevent_manual_evaluation_content_to_organizations.rb
|
654
|
+
- db/migrate/20210119190204_create_exam_registration_exam_join_table.rb
|
640
655
|
- lib/mumuki/domain.rb
|
641
656
|
- lib/mumuki/domain/area.rb
|
642
657
|
- lib/mumuki/domain/engine.rb
|
@@ -669,7 +684,9 @@ files:
|
|
669
684
|
- lib/mumuki/domain/factories/complement_factory.rb
|
670
685
|
- lib/mumuki/domain/factories/course_factory.rb
|
671
686
|
- lib/mumuki/domain/factories/discussion_factory.rb
|
687
|
+
- lib/mumuki/domain/factories/exam_authorization_request_factory.rb
|
672
688
|
- lib/mumuki/domain/factories/exam_factory.rb
|
689
|
+
- lib/mumuki/domain/factories/exam_registration_factory.rb
|
673
690
|
- lib/mumuki/domain/factories/exercise_factory.rb
|
674
691
|
- lib/mumuki/domain/factories/guide_factory.rb
|
675
692
|
- lib/mumuki/domain/factories/invitation_factory.rb
|
@@ -677,6 +694,7 @@ files:
|
|
677
694
|
- lib/mumuki/domain/factories/login_settings_factory.rb
|
678
695
|
- lib/mumuki/domain/factories/medal_factory.rb
|
679
696
|
- lib/mumuki/domain/factories/message_factory.rb
|
697
|
+
- lib/mumuki/domain/factories/notification_factory.rb
|
680
698
|
- lib/mumuki/domain/factories/organization_factory.rb
|
681
699
|
- lib/mumuki/domain/factories/term_factory.rb
|
682
700
|
- lib/mumuki/domain/factories/topic_factory.rb
|