mumuki-domain 7.8.1 → 7.9.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6f501e48aa71e7422741f0223c9ac221b1536bf0171dfd58a5ab2158d4cc215a
4
- data.tar.gz: fe3c3520675d5b9e066dfeb7682d86dc42522afb88d48e3e81245b3f28a750ea
3
+ metadata.gz: e4d324f2eb4b3e06fb42a0aabbbd007c4c828569899c89a789ad681432bb97ba
4
+ data.tar.gz: 3f540fc72a7180e247ee11535316d977920872ecc888c77c7c68346eae0c276e
5
5
  SHA512:
6
- metadata.gz: 7925dac13f2ba1b0e5e5494bd13c167c521fee0dec2ffe5a1024cdc0fc4000ba2ad82786a2d5aa96f354ae147689cc2b763dc03cafa940cd77c1a8fd137e0a71
7
- data.tar.gz: 3f51ba4d74fbd60a6fe1a95e6602ec1539d5a63c185d9468b9a5915af14103d9f264cd0f1a0dc0d7084db444c6304c6129130deab4a5f80f7a6e264a52398070
6
+ metadata.gz: ac7828f09dec30a19e4f4b96bfcfb472fe7fcfc5b53aaeb81e4fb4f5b52533d10b462e9e923d2a2857cec7367cda7d40d6222bd8f8a105dfc84e394b35e552c5
7
+ data.tar.gz: 40cede7e7ff373b1a3a170fbd6bf55bb40faf5c2a814af0849a698f2205a15bff49bca7ba5737577620c48265bad4e42d68e8b4befcb4bbb5d78f9ec82bf23fe
@@ -1,6 +1,7 @@
1
1
  class Assignment < Progress
2
2
  include Contextualization
3
3
  include WithMessages
4
+ include Gamified
4
5
 
5
6
  markdown_on :extra_preview
6
7
 
@@ -41,6 +42,7 @@ class Assignment < Progress
41
42
  alias_method :parent_content, :guide
42
43
  alias_method :user, :submitter
43
44
 
45
+ before_save :award_experience_points!, :update_top_submission!, if: :submission_status_changed?
44
46
  after_save :dirty_parent_by_submission!, if: :completion_changed?
45
47
  before_validation :set_current_organization!, unless: :organization
46
48
 
@@ -161,7 +163,10 @@ class Assignment < Progress
161
163
  end
162
164
 
163
165
  def to_resource_h
164
- as_json(except: %i(exercise_id submission_id organization_id id submitter_id solution created_at updated_at submission_status submitted_at parent_id),
166
+ excluded_fields = %i(created_at exercise_id id organization_id parent_id solution submission_id
167
+ submission_status submitted_at submitter_id top_submission_status updated_at)
168
+
169
+ as_json(except: excluded_fields,
165
170
  include: {
166
171
  guide: {
167
172
  only: [:slug, :name],
@@ -224,6 +229,10 @@ class Assignment < Progress
224
229
  exercise.files_for(current_content)
225
230
  end
226
231
 
232
+ def update_top_submission!
233
+ self.top_submission_status = submission_status unless submission_status.improved_by?(top_submission_status)
234
+ end
235
+
227
236
  private
228
237
 
229
238
  def update_submissions_count!
@@ -1,5 +1,11 @@
1
1
  class Avatar < ApplicationRecord
2
+ include WithTargetAudience
3
+
4
+ def self.sample_for(user)
5
+ with_current_audience_for(user).sample
6
+ end
7
+
2
8
  def self.sample
3
- Avatar.order('RANDOM()').first
9
+ order('RANDOM()').first
4
10
  end
5
11
  end
@@ -16,6 +16,7 @@ module Contextualization
16
16
  end
17
17
 
18
18
  included do
19
+ serialize :top_submission_status, Mumuki::Domain::Status::Submission
19
20
  serialize :submission_status, Mumuki::Domain::Status::Submission
20
21
  validates_presence_of :submission_status
21
22
 
@@ -0,0 +1,15 @@
1
+ module Gamified
2
+ def award_experience_points!
3
+ points = net_experience
4
+
5
+ if points > 0
6
+ stats = UserStats.stats_for(submitter)
7
+ stats.add_exp!(points)
8
+ stats.save!
9
+ end
10
+ end
11
+
12
+ def net_experience
13
+ submission_status.exp_given - top_submission_status.exp_given
14
+ end
15
+ end
@@ -9,7 +9,7 @@ module WithReminders
9
9
  end
10
10
 
11
11
  def remind!
12
- build_reminder.deliver
12
+ build_reminder.deliver_now
13
13
  update! last_reminded_date: Time.now
14
14
  end
15
15
 
@@ -0,0 +1,13 @@
1
+ module WithTargetAudience
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ enum target_audience: [:grown_ups, :kids]
6
+ end
7
+
8
+ class_methods do
9
+ def with_current_audience_for(user)
10
+ where(target_audience: user.current_audience)
11
+ end
12
+ end
13
+ end
@@ -5,6 +5,8 @@ class Organization < ApplicationRecord
5
5
 
6
6
  include Mumukit::Login::OrganizationHelpers
7
7
 
8
+ include WithTargetAudience
9
+
8
10
  serialize :profile, Mumuki::Domain::Organization::Profile
9
11
  serialize :settings, Mumuki::Domain::Organization::Settings
10
12
  serialize :theme, Mumuki::Domain::Organization::Theme
@@ -30,16 +30,15 @@ class User < ApplicationRecord
30
30
 
31
31
  has_many :exams, through: :exam_authorizations
32
32
 
33
- after_initialize :init
34
-
35
33
  enum gender: %i(female male other unspecified)
36
-
37
34
  belongs_to :avatar, optional: true
38
35
 
39
36
  before_validation :set_uid!
40
37
  validates :uid, presence: true
41
38
 
42
39
  after_save :welcome_to_new_organizations!, if: :gained_access_to_new_orga?
40
+ after_initialize :init
41
+ PLACEHOLDER_IMAGE_URL = 'user_shape.png'.freeze
43
42
 
44
43
  resource_fields :uid, :social_id, :email, :permissions, :verified_first_name, :verified_last_name, *profile_fields
45
44
 
@@ -147,10 +146,14 @@ class User < ApplicationRecord
147
146
  exams.any? { |e| e.in_progress_for? self }
148
147
  end
149
148
 
150
- def profile_picture
149
+ def custom_profile_picture
151
150
  avatar&.image_url || image_url
152
151
  end
153
152
 
153
+ def profile_picture
154
+ custom_profile_picture || placeholder_image_url
155
+ end
156
+
154
157
  def bury!
155
158
  # TODO change avatar
156
159
  update! self.class.buried_profile.merge(accepts_reminders: false, gender: nil, birthdate: nil)
@@ -215,11 +218,25 @@ class User < ApplicationRecord
215
218
  false
216
219
  end
217
220
 
221
+ def current_audience
222
+ current_organic_context&.target_audience
223
+ end
224
+
225
+ def placeholder_image_url
226
+ PLACEHOLDER_IMAGE_URL
227
+ end
228
+
229
+ def age
230
+ if birthdate.present?
231
+ @age ||= Time.now.round_years_since(birthdate.to_time)
232
+ end
233
+ end
234
+
218
235
  private
219
236
 
220
237
  def welcome_to_new_organizations!
221
238
  new_accessible_organizations.each do |organization|
222
- UserMailer.welcome_email(self, organization).deliver_later if organization.greet_new_users?
239
+ UserMailer.welcome_email(self, organization).deliver_now rescue nil if organization.greet_new_users?
223
240
  end
224
241
  end
225
242
 
@@ -240,7 +257,10 @@ class User < ApplicationRecord
240
257
  end
241
258
 
242
259
  def init
243
- self.avatar = Avatar.sample unless profile_picture.present?
260
+ if custom_profile_picture.blank?
261
+ self.avatar = Avatar.sample_for(self)
262
+ save if persisted?
263
+ end
244
264
  end
245
265
 
246
266
  def self.sync_key_id_field
@@ -265,4 +285,12 @@ class User < ApplicationRecord
265
285
  def self.buried_profile
266
286
  (@buried_profile || {}).slice(:first_name, :last_name, :email)
267
287
  end
288
+
289
+ def current_organic_context
290
+ if Organization.current?
291
+ Organization.current
292
+ else
293
+ main_organization
294
+ end
295
+ end
268
296
  end
@@ -0,0 +1,16 @@
1
+ class UserStats < ApplicationRecord
2
+ belongs_to :organization
3
+ belongs_to :user
4
+
5
+ def self.stats_for(user)
6
+ UserStats.find_or_initialize_by(user: user, organization: Organization.current)
7
+ end
8
+
9
+ def self.exp_for(user)
10
+ self.stats_for(user).exp
11
+ end
12
+
13
+ def add_exp!(points)
14
+ self.exp += points
15
+ end
16
+ end
@@ -0,0 +1,10 @@
1
+ class CreateUserStats < ActiveRecord::Migration[5.1]
2
+ def change
3
+ create_table :user_stats do |t|
4
+ t.integer :exp, default: 0
5
+
6
+ t.references :user, index: true
7
+ t.references :organization, index: true
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ class AddTopSubmissionStatusToAssignments < ActiveRecord::Migration[5.1]
2
+ def change
3
+ add_column :assignments, :top_submission_status, :integer, default: 0
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ class AddTargetAudienceToOrganizationsAndAvatars < ActiveRecord::Migration[5.1]
2
+ def change
3
+ add_column :organizations, :target_audience, :integer, default: 0
4
+ add_column :avatars, :target_audience, :integer, default: 0
5
+ end
6
+ end
@@ -2,3 +2,4 @@ require_relative './extensions/string'
2
2
  require_relative './extensions/array'
3
3
  require_relative './extensions/module'
4
4
  require_relative './extensions/hash'
5
+ require_relative './extensions/time'
@@ -0,0 +1,5 @@
1
+ class Time
2
+ def round_years_since(another_time)
3
+ (self.to_s(:number).to_i - another_time.to_s(:number).to_i) / 10e9.to_i
4
+ end
5
+ end
@@ -1,5 +1,6 @@
1
1
  require_relative './factories/api_client_factory'
2
2
  require_relative './factories/assignments_factory'
3
+ require_relative './factories/avatar_factory'
3
4
  require_relative './factories/book_factory'
4
5
  require_relative './factories/chapter_factory'
5
6
  require_relative './factories/complement_factory'
@@ -15,4 +16,4 @@ require_relative './factories/message_factory'
15
16
  require_relative './factories/organization_factory'
16
17
  require_relative './factories/topic_factory'
17
18
  require_relative './factories/user_factory'
18
- require_relative './factories/usage_factory'
19
+ require_relative './factories/usage_factory'
@@ -0,0 +1,6 @@
1
+ FactoryBot.define do
2
+ factory :avatar do
3
+ image_url { Faker::Internet.url }
4
+ target_audience { :grown_ups }
5
+ end
6
+ end
@@ -6,6 +6,5 @@ FactoryBot.define do
6
6
  last_name { Faker::Name.last_name }
7
7
  gender { 1 }
8
8
  birthdate { Date.today }
9
- avatar { Avatar.new image_url: 'user_shape.png' }
10
9
  end
11
10
  end
@@ -66,6 +66,10 @@ module Mumuki::Domain::Helpers::Organization
66
66
  Mumukit::Platform::Organization.current
67
67
  end
68
68
 
69
+ def current?
70
+ Mumukit::Platform::Organization.current?
71
+ end
72
+
69
73
  def parse(json)
70
74
  json
71
75
  .slice(:name)
@@ -13,6 +13,8 @@ module Mumuki::Domain::Helpers::User
13
13
  :protect!,
14
14
  :protect_delegation!,
15
15
  :protect_permissions_assignment!,
16
+ :student_granted_organizations,
17
+ :any_granted_organizations,
16
18
  to: :permissions
17
19
 
18
20
  def platform_class_name
@@ -71,12 +73,10 @@ module Mumuki::Domain::Helpers::User
71
73
  "#{full_name} <#{email}> [#{uid}]"
72
74
  end
73
75
 
74
- ## Accesible organizations
76
+ ## Accessible organizations
75
77
 
76
- def student_granted_organizations
77
- permissions.student_granted_organizations.map do |org|
78
- Mumukit::Platform::Organization.find_by_name!(org) rescue nil
79
- end.compact
78
+ revamp_accessor :any_granted_organizations, :student_granted_organizations do |_, _, result|
79
+ result.map { |org| Mumukit::Platform::Organization.find_by_name!(org) rescue nil }.compact
80
80
  end
81
81
 
82
82
  def has_student_granted_organizations?
@@ -84,11 +84,7 @@ module Mumuki::Domain::Helpers::User
84
84
  end
85
85
 
86
86
  def main_organization
87
- student_granted_organizations.first
88
- end
89
-
90
- def has_main_organization?
91
- student_granted_organizations.length == 1
87
+ student_granted_organizations.first || any_granted_organizations.first
92
88
  end
93
89
 
94
90
  def has_immersive_main_organization?
@@ -40,6 +40,10 @@ module Mumuki::Domain
40
40
  false
41
41
  end
42
42
 
43
+ def can_discuss_in?(*)
44
+ false
45
+ end
46
+
43
47
  # ========
44
48
  # Visiting
45
49
  # ========
@@ -88,6 +92,10 @@ module Mumuki::Domain
88
92
  raise "unknown attribute #{key}"
89
93
  end
90
94
 
95
+ def new_record?
96
+ false
97
+ end
98
+
91
99
  def self.primary_key
92
100
  'id'
93
101
  end
@@ -105,7 +113,6 @@ module Mumuki::Domain
105
113
  assignment.assign_attributes results
106
114
  results
107
115
  end
108
-
109
116
  end
110
117
 
111
118
  Incognito = IncognitoClass.new
@@ -8,4 +8,8 @@ module Mumuki::Domain::Status::Submission::Passed
8
8
  def self.iconize
9
9
  {class: :success, type: 'check-circle'}
10
10
  end
11
+
12
+ def self.exp_given
13
+ 100
14
+ end
11
15
  end
@@ -12,4 +12,8 @@ module Mumuki::Domain::Status::Submission::PassedWithWarnings
12
12
  def self.iconize
13
13
  {class: :warning, type: 'exclamation-circle'}
14
14
  end
15
+
16
+ def self.exp_given
17
+ 50
18
+ end
15
19
  end
@@ -8,4 +8,8 @@ module Mumuki::Domain::Status::Submission::Skipped
8
8
  def self.iconize
9
9
  {class: :success, type: 'check-circle'}
10
10
  end
11
+
12
+ def self.exp_given
13
+ 100
14
+ end
11
15
  end
@@ -42,4 +42,12 @@ module Mumuki::Domain::Status::Submission
42
42
  def solved?
43
43
  passed? || skipped?
44
44
  end
45
+
46
+ def improved_by?(status)
47
+ self.exp_given < status.exp_given
48
+ end
49
+
50
+ def exp_given
51
+ 0
52
+ end
45
53
  end
@@ -1,5 +1,5 @@
1
1
  module Mumuki
2
2
  module Domain
3
- VERSION = '7.8.1'
3
+ VERSION = '7.9.0'
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mumuki-domain
3
3
  version: !ruby/object:Gem::Version
4
- version: 7.8.1
4
+ version: 7.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Franco Leonardo Bulgarelli
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-08-31 00:00:00.000000000 Z
11
+ date: 2020-09-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -256,6 +256,7 @@ files:
256
256
  - app/models/concerns/contextualization.rb
257
257
  - app/models/concerns/disabling.rb
258
258
  - app/models/concerns/friendly_name.rb
259
+ - app/models/concerns/gamified.rb
259
260
  - app/models/concerns/guide_container.rb
260
261
  - app/models/concerns/navigation/parent_navigation.rb
261
262
  - app/models/concerns/navigation/siblings_navigation.rb
@@ -292,6 +293,7 @@ files:
292
293
  - app/models/concerns/with_scoped_queries/page.rb
293
294
  - app/models/concerns/with_scoped_queries/sort.rb
294
295
  - app/models/concerns/with_slug.rb
296
+ - app/models/concerns/with_target_audience.rb
295
297
  - app/models/concerns/with_usages.rb
296
298
  - app/models/concerns/with_user_navigation.rb
297
299
  - app/models/content.rb
@@ -322,6 +324,7 @@ files:
322
324
  - app/models/upvote.rb
323
325
  - app/models/usage.rb
324
326
  - app/models/user.rb
327
+ - app/models/user_stats.rb
325
328
  - app/models/with_stats.rb
326
329
  - db/migrate/20141120231135_create_exercises.rb
327
330
  - db/migrate/20141120231735_create_submissions.rb
@@ -608,7 +611,10 @@ files:
608
611
  - db/migrate/20200601203033_add_course_to_exam.rb
609
612
  - db/migrate/20200605161350_add_passing_criterions_to_exam.rb
610
613
  - db/migrate/20200608132959_add_progressive_display_lookahead_to_organizations.rb
614
+ - db/migrate/20200616160640_create_user_stats.rb
615
+ - db/migrate/20200617142217_add_top_submission_status_to_assignments.rb
611
616
  - db/migrate/20200702165503_add_messages_count_to_discussion.rb
617
+ - db/migrate/20200717143830_add_target_audience_to_organizations_and_avatars.rb
612
618
  - db/migrate/20200728162727_add_not_actually_a_question_field_to_messages.rb
613
619
  - db/migrate/20200728163038_add_requires_moderator_response_to_discussions.rb
614
620
  - db/migrate/20200730221001_add_trusted_for_forum_to_user.rb
@@ -634,9 +640,11 @@ files:
634
640
  - lib/mumuki/domain/extensions/hash.rb
635
641
  - lib/mumuki/domain/extensions/module.rb
636
642
  - lib/mumuki/domain/extensions/string.rb
643
+ - lib/mumuki/domain/extensions/time.rb
637
644
  - lib/mumuki/domain/factories.rb
638
645
  - lib/mumuki/domain/factories/api_client_factory.rb
639
646
  - lib/mumuki/domain/factories/assignments_factory.rb
647
+ - lib/mumuki/domain/factories/avatar_factory.rb
640
648
  - lib/mumuki/domain/factories/book_factory.rb
641
649
  - lib/mumuki/domain/factories/chapter_factory.rb
642
650
  - lib/mumuki/domain/factories/complement_factory.rb