Class: User
- Inherits:
-
ApplicationRecord
show all
- Includes:
- Awardee, Mumuki::Domain::Helpers::User, Mumuki::Domain::Syncable, Onomastic, WithDeletedUser, WithDiscussionCreation, WithNotifications, WithPreferences, WithProfile, WithReminders, WithTermsAcceptance, WithUserNavigation
- Defined in:
- app/models/user.rb
Constant Summary
collapse
- PLACEHOLDER_IMAGE_URL =
'user_shape.png'.freeze
Instance Method Summary
collapse
-
#abbreviated_name ⇒ Object
-
#accept_invitation!(invitation) ⇒ Object
-
#age ⇒ Object
-
#attach!(role, course) ⇒ Object
-
#build_assignment(exercise, organization) ⇒ Object
-
#bury! ⇒ Object
-
#can_access_teacher_info_in?(organization) ⇒ Boolean
-
#can_discuss_here? ⇒ Boolean
-
#can_discuss_in?(organization) ⇒ Boolean
-
#certificate_in(certificate_program, certificate_h) ⇒ Object
-
#certificated_in?(certificate_program) ⇒ Boolean
-
#certificates_in_organization(organization = Organization.current) ⇒ Object
-
#clean_belongings! ⇒ Object
-
#clear_progress! ⇒ Object
-
#clear_progress_for!(organization) ⇒ Object
-
#completed_containers(sequence, organization) ⇒ Object
Takes a didactic - ordered - sequence of content containers and returns those that have been completed.
-
#completed_containers_with_lookahead(sequence, organization, lookahead: 1) ⇒ Object
Like ‘completed_containers`, returns a slice of the completed containers in the sequence, but adding a configurable number of trailing, non-completed contaienrs.
-
#current_audience ⇒ Object
-
#current_immersive_context_and_content_at(path_item) ⇒ Object
-
#current_immersive_context_at(path_item) ⇒ Object
-
#current_organic_context ⇒ Object
-
#currently_in_exam? ⇒ Boolean
-
#custom_profile_picture ⇒ Object
-
#detach!(role, course, deep: false) ⇒ Object
-
#has_assignments_in_organization?(organization) ⇒ Boolean
-
#ignores_notification?(notification) ⇒ Boolean
-
#import_from_resource_h!(json) ⇒ Object
-
#incognito? ⇒ Boolean
-
#interpolations ⇒ Object
-
#last_lesson ⇒ Object
-
#messages_in_organization(organization = Organization.current) ⇒ Object
-
#name_initials ⇒ Object
-
#never_submitted? ⇒ Boolean
-
#next_exercise_at(guide) ⇒ Object
-
#notify_permissions_changed! ⇒ Object
-
#notify_via_email!(notification) ⇒ Object
-
#passed_assignments ⇒ Object
-
#passed_submissions_count ⇒ Object
-
#passed_submissions_count_in(organization) ⇒ Object
-
#pending_siblings_at(content) ⇒ Object
-
#placeholder_image_url ⇒ Object
-
#profile_picture ⇒ Object
-
#progress_at(content, organization) ⇒ Object
-
#restore_organization_progress!(organization) ⇒ Object
-
#run_submission!(submission, assignment, evaluation) ⇒ Object
-
#save_and_notify! ⇒ Object
-
#solved_any_exercises?(organization = Organization.current) ⇒ Boolean
-
#solved_exercises_count ⇒ Object
-
#submissions_count ⇒ Object
-
#submitted_exercises_count ⇒ Object
-
#to_resource_h ⇒ Object
-
#to_s ⇒ Object
-
#transfer_progress_to!(another) ⇒ Object
-
#trusted_as_discusser_in?(organization) ⇒ Boolean
-
#unsubscribe_from_reminders! ⇒ Object
-
#verify_name!(force: false) ⇒ Object
-
#visit!(organization) ⇒ Object
#navigable_name, #navigation_end?
#build_reminder, #remind!, #should_remind?, #try_remind_with_lock!
#read_notification!, #unread_messages, #unread_notifications
Methods included from Awardee
#acquired_medals, #unacquired_medals
#accept_forum_terms!, #accept_profile_terms!, #forum_terms, #has_accepted?, #has_forum_terms_to_accept?, #has_profile_terms_to_accept?, #has_role_terms_to_accept?, #profile_terms, #role_specific_terms
Methods included from Onomastic
#formal_first_name, #formal_full_name, #formal_last_name, #full_name, #has_verified_full_name?, #verified_full_name
#any_granted_organizations, #discusser_of?, #has_immersive_main_organization?, #has_role?, #has_student_granted_organizations?, #immersive_organization_at, #immersive_organization_with_content_at, #immersive_organizations_at, #immersive_organizations_with_content_at, #main_organization, #merge_permissions!, #platform_class_name, #profile_completed?, #to_param
#platform_class_name, #sync_key
#deleted_user?, prepended
active_between, aggregate_of, all_except, defaults, #delete, #destroy!, enum_prefixed_translations_for, numbered, organic_on, resource_fields, #save, #save_and_notify_changes!, serialize_symbolized_hash_array, teaser_on, #update_and_notify!, update_or_create!, whitelist_attributes, with_pg_retry, with_temporary_token
Methods included from WithPgLock
#with_pg_lock
Instance Method Details
#abbreviated_name ⇒ Object
236
237
238
|
# File 'app/models/user.rb', line 236
def abbreviated_name
"#{first_name} #{last_name.first.capitalize + '.' if last_name.present?}".strip
end
|
#accept_invitation!(invitation) ⇒ Object
126
127
128
|
# File 'app/models/user.rb', line 126
def accept_invitation!(invitation)
make_student_of! invitation.course_slug
end
|
#age ⇒ Object
272
273
274
275
276
|
# File 'app/models/user.rb', line 272
def age
if birthdate.present?
@age ||= Time.current.round_years_since(birthdate.in_time_zone)
end
end
|
#attach!(role, course) ⇒ Object
165
166
167
168
|
# File 'app/models/user.rb', line 165
def attach!(role, course)
add_permission! role, course.slug
save_and_notify!
end
|
#build_assignment(exercise, organization) ⇒ Object
244
245
246
|
# File 'app/models/user.rb', line 244
def build_assignment(exercise, organization)
Assignment.build_for(self, exercise, organization)
end
|
#bury! ⇒ Object
196
197
198
199
|
# File 'app/models/user.rb', line 196
def bury!
update! self.class.buried_profile.merge(accepts_reminders: false, gender: nil, birthdate: nil)
end
|
#can_access_teacher_info_in?(organization) ⇒ Boolean
228
229
230
|
# File 'app/models/user.rb', line 228
def can_access_teacher_info_in?(organization)
teacher_of?(organization) || organization.teacher_training?
end
|
#can_discuss_here? ⇒ Boolean
224
225
226
|
# File 'app/models/user.rb', line 224
def can_discuss_here?
can_discuss_in? Organization.current
end
|
#can_discuss_in?(organization) ⇒ Boolean
216
217
218
|
# File 'app/models/user.rb', line 216
def can_discuss_in?(organization)
organization.access_mode(self).discuss_here?
end
|
#certificate_in(certificate_program, certificate_h) ⇒ Object
323
324
325
326
327
|
# File 'app/models/user.rb', line 323
def certificate_in(certificate_program, certificate_h)
return if certificated_in?(certificate_program)
certificate = certificates.create certificate_h.merge(certificate_program: certificate_program)
UserMailer.certificate(certificate).post!
end
|
#certificated_in?(certificate_program) ⇒ Boolean
319
320
321
|
# File 'app/models/user.rb', line 319
def certificated_in?(certificate_program)
certificates.where(certificate_program: certificate_program).exists?
end
|
#certificates_in_organization(organization = Organization.current) ⇒ Object
315
316
317
|
# File 'app/models/user.rb', line 315
def certificates_in_organization(organization = Organization.current)
certificates.where certificate_program: CertificateProgram.where(organization: organization)
end
|
#clean_belongings! ⇒ Object
349
350
351
352
353
|
# File 'app/models/user.rb', line 349
def clean_belongings!
discussions.update_all initiator_id: User.deleted_user.id
forum_messages.update_all sender_id: User.deleted_user.id
direct_messages.where(sender: self).delete_all
end
|
#clear_progress! ⇒ Object
109
110
111
112
113
114
|
# File 'app/models/user.rb', line 109
def clear_progress!
transaction do
update! last_submission_date: nil, last_exercise: nil
assignments.destroy_all
end
end
|
#clear_progress_for!(organization) ⇒ Object
329
330
331
332
333
334
335
336
337
338
339
|
# File 'app/models/user.rb', line 329
def clear_progress_for!(organization)
location = { organization: organization }.compact
target_assignments = assignments.where(location)
direct_messages.where(assignment: target_assignments).delete_all
target_assignments.delete_all
indicators.where(location).delete_all
user_stats.where(location).delete_all
end
|
#completed_containers(sequence, organization) ⇒ Object
Takes a didactic - ordered - sequence of content containers and returns those that have been completed
203
204
205
|
# File 'app/models/user.rb', line 203
def completed_containers(sequence, organization)
sequence.take_while { |it| it.content.completed_for?(self, organization) }
end
|
#completed_containers_with_lookahead(sequence, organization, lookahead: 1) ⇒ Object
Like ‘completed_containers`, returns a slice of the completed containers in the sequence, but adding a configurable number of trailing, non-completed contaienrs
209
210
211
212
213
214
|
# File 'app/models/user.rb', line 209
def completed_containers_with_lookahead(sequence, organization, lookahead: 1)
raise 'invalid lookahead' if lookahead < 1
count = completed_containers(sequence, organization).size
sequence[0..count + lookahead - 1]
end
|
#current_audience ⇒ Object
264
265
266
|
# File 'app/models/user.rb', line 264
def current_audience
current_organic_context&.target_audience
end
|
#current_immersive_context_and_content_at(path_item) ⇒ Object
309
310
311
312
313
|
# File 'app/models/user.rb', line 309
def current_immersive_context_and_content_at(path_item)
immersive_organization_with_content_at(path_item).tap do |orga, _|
return [Organization.current, path_item] unless orga.present?
end
end
|
#current_immersive_context_at(path_item) ⇒ Object
282
283
284
285
286
287
288
|
# File 'app/models/user.rb', line 282
def current_immersive_context_at(path_item)
if Organization.current?
immersive_organization_at(path_item) || Organization.current
else
main_organization
end
end
|
#current_organic_context ⇒ Object
278
279
280
|
# File 'app/models/user.rb', line 278
def current_organic_context
Organization.current? ? Organization.current : main_organization
end
|
#currently_in_exam? ⇒ Boolean
184
185
186
|
# File 'app/models/user.rb', line 184
def currently_in_exam?
exams.any? { |e| e.in_progress_for? self }
end
|
#custom_profile_picture ⇒ Object
188
189
190
|
# File 'app/models/user.rb', line 188
def custom_profile_picture
avatar&.image_url || image_url
end
|
#detach!(role, course, deep: false) ⇒ Object
170
171
172
173
174
|
# File 'app/models/user.rb', line 170
def detach!(role, course, deep: false)
make_ex_student_of! course.slug if !deep && student_of?(course.slug) && solved_any_exercises?(course.organization)
remove_permission! role, course.slug
save_and_notify!
end
|
#has_assignments_in_organization?(organization) ⇒ Boolean
122
123
124
|
# File 'app/models/user.rb', line 122
def has_assignments_in_organization?(organization)
assignments_in_organization(organization).exists?
end
|
#ignores_notification?(notification) ⇒ Boolean
345
346
347
|
# File 'app/models/user.rb', line 345
def ignores_notification?(notification)
ignored_notifications.include? notification.subject
end
|
#import_from_resource_h!(json) ⇒ Object
142
143
144
|
# File 'app/models/user.rb', line 142
def import_from_resource_h!(json)
update! self.class.slice_resource_h json
end
|
#incognito? ⇒ Boolean
260
261
262
|
# File 'app/models/user.rb', line 260
def incognito?
false
end
|
#interpolations ⇒ Object
176
177
178
179
180
181
182
|
# File 'app/models/user.rb', line 176
def interpolations
{
'user_email' => email,
'user_first_name' => first_name,
'user_last_name' => last_name
}
end
|
#last_lesson ⇒ Object
65
66
67
|
# File 'app/models/user.rb', line 65
def last_lesson
last_guide.try(:lesson)
end
|
#messages_in_organization(organization = Organization.current) ⇒ Object
69
70
71
|
# File 'app/models/user.rb', line 69
def messages_in_organization(organization = Organization.current)
direct_messages.where('assignments.organization': organization)
end
|
#name_initials ⇒ Object
232
233
234
|
# File 'app/models/user.rb', line 232
def name_initials
name.split.map(&:first).map(&:capitalize).join(' ')
end
|
#never_submitted? ⇒ Boolean
105
106
107
|
# File 'app/models/user.rb', line 105
def never_submitted?
last_submission_date.nil?
end
|
#next_exercise_at(guide) ⇒ Object
252
253
254
|
# File 'app/models/user.rb', line 252
def next_exercise_at(guide)
guide.pending_exercises(self).order('exercises.number asc').first
end
|
#notify_permissions_changed! ⇒ Object
290
291
292
293
294
295
296
297
|
# File 'app/models/user.rb', line 290
def notify_permissions_changed!
return if permissions_before_last_save == permissions
Mumukit::Nuntius.notify! 'user-permissions-changed', user: {
uid: uid,
old_permissions: permissions_before_last_save.as_json,
new_permissions: permissions.as_json
}
end
|
#notify_via_email!(notification) ⇒ Object
341
342
343
|
# File 'app/models/user.rb', line 341
def notify_via_email!(notification)
UserMailer.notification(notification).post! unless ignores_notification? notification
end
|
#passed_assignments ⇒ Object
#passed_submissions_count ⇒ Object
81
82
83
|
# File 'app/models/user.rb', line 81
def passed_submissions_count
passed_assignments.count
end
|
#passed_submissions_count_in(organization) ⇒ Object
73
74
75
|
# File 'app/models/user.rb', line 73
def passed_submissions_count_in(organization)
assignments.where(top_submission_status: Mumuki::Domain::Status::Submission::Passed.to_i, organization: organization).count
end
|
#pending_siblings_at(content) ⇒ Object
248
249
250
|
# File 'app/models/user.rb', line 248
def pending_siblings_at(content)
content.pending_siblings_for(self)
end
|
#placeholder_image_url ⇒ Object
#profile_picture ⇒ Object
192
193
194
|
# File 'app/models/user.rb', line 192
def profile_picture
custom_profile_picture || placeholder_image_url
end
|
#progress_at(content, organization) ⇒ Object
240
241
242
|
# File 'app/models/user.rb', line 240
def progress_at(content, organization)
Indicator.find_or_initialize_by(user: self, organization: organization, content: content)
end
|
#restore_organization_progress!(organization) ⇒ Object
116
117
118
119
120
|
# File 'app/models/user.rb', line 116
def restore_organization_progress!(organization)
assignments_in_organization(organization).each do |assignment|
assignment.tap(&:parent).save!
end
end
|
#run_submission!(submission, assignment, evaluation) ⇒ Object
256
257
258
|
# File 'app/models/user.rb', line 256
def run_submission!(submission, assignment, evaluation)
submission.run! assignment, evaluation
end
|
#save_and_notify! ⇒ Object
303
304
305
306
307
|
# File 'app/models/user.rb', line 303
def save_and_notify!
save!
notify_permissions_changed!
self
end
|
#solved_any_exercises?(organization = Organization.current) ⇒ Boolean
299
300
301
|
# File 'app/models/user.rb', line 299
def solved_any_exercises?(organization = Organization.current)
Assignment.exists?(organization: organization, submitter: self)
end
|
#solved_exercises_count ⇒ Object
89
90
91
|
# File 'app/models/user.rb', line 89
def solved_exercises_count
solved_exercises.count
end
|
#submissions_count ⇒ Object
77
78
79
|
# File 'app/models/user.rb', line 77
def submissions_count
assignments.pluck(:submissions_count).sum
end
|
#submitted_exercises_count ⇒ Object
85
86
87
|
# File 'app/models/user.rb', line 85
def submitted_exercises_count
submitted_exercises.count
end
|
#to_resource_h ⇒ Object
146
147
148
|
# File 'app/models/user.rb', line 146
def to_resource_h
super.merge(image_url: profile_picture)
end
|
#to_s ⇒ Object
101
102
103
|
# File 'app/models/user.rb', line 101
def to_s
"#{id}:#{name}:#{uid}"
end
|
#transfer_progress_to!(another) ⇒ Object
130
131
132
133
134
135
136
137
138
139
140
|
# File 'app/models/user.rb', line 130
def transfer_progress_to!(another)
transaction do
assignments.update_all(submitter_id: another.id)
if another.never_submitted? || last_submission_date.try { |it| it > another.last_submission_date }
another.update! last_submission_date: last_submission_date,
last_exercise: last_exercise,
last_organization: last_organization
end
end
reload
end
|
#trusted_as_discusser_in?(organization) ⇒ Boolean
220
221
222
|
# File 'app/models/user.rb', line 220
def trusted_as_discusser_in?(organization)
trusted_for_forum? || !organization.forum_only_for_trusted?
end
|
#unsubscribe_from_reminders! ⇒ Object
161
162
163
|
# File 'app/models/user.rb', line 161
def unsubscribe_from_reminders!
update! accepts_reminders: false
end
|
#verify_name!(force: false) ⇒ Object
150
151
152
153
154
155
156
157
158
159
|
# File 'app/models/user.rb', line 150
def verify_name!(force: false)
if force
self.verified_first_name = first_name
self.verified_last_name = last_name
else
self.verified_first_name ||= first_name
self.verified_last_name ||= last_name
end
save!
end
|
#visit!(organization) ⇒ Object
97
98
99
|
# File 'app/models/user.rb', line 97
def visit!(organization)
update!(last_organization: organization) if organization != last_organization
end
|