mumuki-domain 6.7.2 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/app/models/application_record.rb +11 -0
  4. data/app/models/assignment.rb +7 -1
  5. data/app/models/concerns/with_assignments.rb +3 -3
  6. data/app/models/content.rb +1 -1
  7. data/app/models/course.rb +5 -3
  8. data/app/models/exercise.rb +2 -0
  9. data/app/models/guide.rb +4 -2
  10. data/app/models/invitation.rb +1 -1
  11. data/app/models/language.rb +28 -8
  12. data/app/models/organization.rb +15 -7
  13. data/app/models/user.rb +7 -3
  14. data/db/migrate/20170621222902_introduce_settings_and_themes.rb +3 -3
  15. data/db/migrate/20190530173142_add_organization_to_assignment.rb +5 -0
  16. data/db/migrate/20190702003600_add_loading_flags_to_language.rb +6 -0
  17. data/db/migrate/20190702182407_add_new_profile_fields.rb +6 -0
  18. data/lib/mumuki/domain/extensions/module.rb +17 -0
  19. data/lib/mumuki/domain/extensions.rb +1 -0
  20. data/lib/mumuki/domain/factories/assignments_factory.rb +1 -0
  21. data/lib/mumuki/domain/factories/organization_factory.rb +17 -6
  22. data/lib/mumuki/domain/factories/user_factory.rb +2 -0
  23. data/lib/mumuki/domain/helpers/course.rb +13 -0
  24. data/lib/mumuki/domain/helpers/organization.rb +72 -0
  25. data/lib/mumuki/domain/helpers/user.rb +109 -0
  26. data/lib/mumuki/domain/helpers.rb +8 -0
  27. data/lib/mumuki/domain/organization/profile.rb +39 -0
  28. data/lib/mumuki/domain/organization/settings.rb +27 -0
  29. data/lib/mumuki/domain/organization/theme.rb +4 -0
  30. data/lib/mumuki/domain/organization.rb +8 -0
  31. data/lib/mumuki/domain/stores/bibliotheca_store.rb +29 -0
  32. data/lib/mumuki/domain/stores/exercise_schema.rb +37 -0
  33. data/lib/mumuki/domain/stores/guide_schema.rb +34 -0
  34. data/lib/mumuki/domain/stores/thesaurus_store.rb +79 -0
  35. data/lib/mumuki/domain/stores.rb +4 -0
  36. data/lib/mumuki/domain/syncable/with_resource_fields.rb +18 -0
  37. data/{app/models/concerns → lib/mumuki/domain}/syncable.rb +3 -1
  38. data/lib/mumuki/domain/version.rb +1 -1
  39. data/lib/mumuki/domain.rb +10 -0
  40. metadata +31 -12
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2e24945e78e243ddb96ddee6a26d03b3153269eb10d3a88cb8a8e8c351bd33c4
4
- data.tar.gz: 5bdc4f8615625d4970a557be4ae10057cc1174eaae506a2ed59e1d77e433e325
3
+ metadata.gz: dd3eb525947de7658551f36161b8f937d6d9e192b02c9f9a60f72e5170a612f4
4
+ data.tar.gz: 1c2a08701b38e3aa25ce125739359500a727582c3d42ddf2f4ad7b3df537df36
5
5
  SHA512:
6
- metadata.gz: cbaaa6db817031fc9cd233636501bc0f012a3d810510e62a29d6fdce06013c3bee5c92e46930951d5108bb95cade1314366930550bf7ba3830aaa54e8cb03354
7
- data.tar.gz: c7cb9c09c05c01db817d57cc5abda3524a2a69e783335a9d752795f5b16a94cedc14b09d35304b2869799324cb6bbfc6903e9075aea9491f95719ed987c84564
6
+ metadata.gz: 9cae5a11974987b65a5231351c7e2e51c6c05d9d11071f98a3ecf34fb5aadb909093d3fb2d1340e7d8ba78c778baf2504e74985d741b49d7c985652d848def3e
7
+ data.tar.gz: 6e39bad09b6c1c4fe9afc57ddf8d77407253d9fe6e5c38fde08223d02c8d10710357b011b8009d553fadf1d106b4798247e9f09d5310dbf8f85e98e5c237dfb0
data/README.md CHANGED
@@ -43,7 +43,7 @@ _as defined in `Language#to_resource_h`_
43
43
  theme
44
44
  ```
45
45
 
46
- _as defined in `Mumukit::Platform::Organization::Helpers#to_resource_h`_
46
+ _as defined in `Mumuki::Domain::Helpers::Organization#to_resource_h`_
47
47
 
48
48
  ### `User`
49
49
 
@@ -57,7 +57,7 @@ _as defined in `Mumukit::Platform::Organization::Helpers#to_resource_h`_
57
57
  permissions
58
58
  ```
59
59
 
60
- _as defined in `Mumukit::Platform::User::Helpers#to_resource_h`_
60
+ _as defined in `Mumuki::Domain::Helpers::User#to_resource_h`_
61
61
 
62
62
  ### `Course`
63
63
 
@@ -70,7 +70,7 @@ _as defined in `Mumukit::Platform::User::Helpers#to_resource_h`_
70
70
  description
71
71
  ```
72
72
 
73
- _as defined in `Mumukit::Platform::Course::Helpers#to_resource_h`_
73
+ _as defined in `Mumuki::Domain::Helpers::Course#to_resource_h`_
74
74
 
75
75
  ## Content Hashes
76
76
 
@@ -109,6 +109,17 @@ class ApplicationRecord < ActiveRecord::Base
109
109
  end
110
110
  end
111
111
 
112
+ ## Partially implements resource-hash protocol, by
113
+ ## defining `to_resource_h` and helper methods `resource_fields` and `slice_resource_h`
114
+ ## using the given fields
115
+ def self.resource_fields(*fields)
116
+ include Mumuki::Domain::Syncable::WithResourceFields
117
+
118
+ define_singleton_method :resource_fields do
119
+ fields
120
+ end
121
+ end
122
+
112
123
  private
113
124
 
114
125
  def raise_foreign_key_error!
@@ -12,6 +12,7 @@ class Assignment < ApplicationRecord
12
12
  primary_key: :submission_id,
13
13
  dependent: :destroy
14
14
 
15
+ belongs_to :organization
15
16
  belongs_to :submitter, class_name: 'User'
16
17
 
17
18
  validates_presence_of :exercise, :submitter
@@ -126,7 +127,7 @@ class Assignment < ApplicationRecord
126
127
  end
127
128
 
128
129
  def to_resource_h
129
- as_json(except: [:exercise_id, :submission_id, :id, :submitter_id, :solution, :created_at, :updated_at, :submission_status],
130
+ as_json(except: [:exercise_id, :submission_id, :organization_id, :id, :submitter_id, :solution, :created_at, :updated_at, :submission_status],
130
131
  include: {
131
132
  guide: {
132
133
  only: [:slug, :name],
@@ -189,6 +190,11 @@ class Assignment < ApplicationRecord
189
190
  exercise.files_for(current_content)
190
191
  end
191
192
 
193
+ def save!(*)
194
+ self.organization = Organization.current
195
+ super
196
+ end
197
+
192
198
  private
193
199
 
194
200
  def update_submissions_count!
@@ -18,7 +18,7 @@ module WithAssignments
18
18
  messages_for(user).present?
19
19
  end
20
20
 
21
- def find_assignment_for(user)
21
+ def find_assignment_for(user, _organization)
22
22
  assignments.find_by(submitter: user)
23
23
  end
24
24
 
@@ -26,7 +26,7 @@ module WithAssignments
26
26
  assignment_for(user).status if user
27
27
  end
28
28
 
29
- def assignment_for(user)
30
- find_assignment_for(user) || user.assignments.build(exercise: self)
29
+ def assignment_for(user, organization: Organization.current)
30
+ find_assignment_for(user, organization) || user.assignments.build(exercise: self, organization: organization)
31
31
  end
32
32
  end
@@ -1,7 +1,7 @@
1
1
  class Content < ApplicationRecord
2
2
  self.abstract_class = true
3
3
 
4
- include Syncable
4
+ include Mumuki::Domain::Syncable
5
5
  include WithDescription
6
6
  include WithLocale
7
7
  include WithSlug
data/app/models/course.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  class Course < ApplicationRecord
2
- include Syncable
3
- include Mumukit::Platform::Course::Helpers
2
+ include Mumuki::Domain::Syncable
3
+ include Mumuki::Domain::Helpers::Course
4
4
 
5
5
  validates_presence_of :slug, :shifts, :code, :days, :period, :description, :organization_id
6
6
  validates_uniqueness_of :slug
@@ -10,12 +10,14 @@ class Course < ApplicationRecord
10
10
 
11
11
  alias_attribute :name, :code
12
12
 
13
+ resource_fields :slug, :shifts, :code, :days, :period, :description
14
+
13
15
  def current_invitation
14
16
  invitations.where('expiration_date > ?', Time.now).take
15
17
  end
16
18
 
17
19
  def import_from_resource_h!(resource_h)
18
- update! Mumukit::Platform::Course::Helpers.slice_resource_h(resource_h)
20
+ update! self.class.slice_resource_h(resource_h)
19
21
  end
20
22
 
21
23
  def slug=(slug)
@@ -111,6 +111,8 @@ class Exercise < ApplicationRecord
111
111
  choice_values.index(value)
112
112
  end
113
113
 
114
+ # Keep this list up to date with
115
+ # Mumukit::Sync::Store::Github::Schema::Exercise
114
116
  def to_resource_h
115
117
  language_resource_h = language.to_embedded_resource_h if language != guide.language
116
118
  as_json(only: %i(name layout editor corollary teacher_info manual_evaluation locale
data/app/models/guide.rb CHANGED
@@ -14,10 +14,10 @@ class Guide < Content
14
14
 
15
15
  enum type: [:learning, :practice]
16
16
 
17
- def clear_progress!(user)
17
+ def clear_progress!(user, organization)
18
18
  transaction do
19
19
  exercises.each do |exercise|
20
- exercise.find_assignment_for(user)&.destroy!
20
+ exercise.find_assignment_for(user, organization)&.destroy!
21
21
  end
22
22
  end
23
23
  end
@@ -86,6 +86,8 @@ class Guide < Content
86
86
  reload
87
87
  end
88
88
 
89
+ # Keep this list up to date with
90
+ # Mumukit::Sync::Store::Github::Schema::Guide
89
91
  def to_resource_h
90
92
  as_json(only: %i(beta type id_format private expectations corollary teacher_info sources learn_more authors collaborators extra settings))
91
93
  .symbolize_keys
@@ -1,5 +1,5 @@
1
1
  class Invitation < ApplicationRecord
2
- include Syncable
2
+ include Mumuki::Domain::Syncable
3
3
 
4
4
  belongs_to :course
5
5
  validates_uniqueness_of :code
@@ -1,6 +1,6 @@
1
1
  class Language < ApplicationRecord
2
2
  include WithCaseInsensitiveSearch
3
- include Syncable
3
+ include Mumuki::Domain::Syncable
4
4
 
5
5
  enum output_content_type: [:plain, :html, :markdown]
6
6
 
@@ -8,6 +8,33 @@ class Language < ApplicationRecord
8
8
 
9
9
  validates :name, presence: true, uniqueness: {case_sensitive: false}
10
10
 
11
+ # This list must kept up to date with
12
+ # Mumukit::Sync::Store::Thesaurus::InfoConverter
13
+ resource_fields :comment_type,
14
+ :devicon,
15
+ :editor_css_urls,
16
+ :editor_html_urls,
17
+ :editor_js_urls,
18
+ :editor_shows_loading_content,
19
+ :extension,
20
+ :feedback,
21
+ :highlight_mode,
22
+ :layout_css_urls,
23
+ :layout_html_urls,
24
+ :layout_js_urls,
25
+ :layout_shows_loading_content,
26
+ :multifile,
27
+ :name,
28
+ :output_content_type,
29
+ :prompt,
30
+ :queriable,
31
+ :runner_url,
32
+ :stateful_console,
33
+ :test_extension,
34
+ :test_template,
35
+ :triable,
36
+ :visible_success_output
37
+
11
38
  markdown_on :description
12
39
 
13
40
  delegate :run_tests!, :run_query!, :run_try!, to: :bridge
@@ -64,13 +91,6 @@ class Language < ApplicationRecord
64
91
  as_json(only: [:name, :extension, :test_extension]).symbolize_keys
65
92
  end
66
93
 
67
- def to_resource_h
68
- as_json(only: %i(comment_type devicon editor_css_urls editor_html_urls editor_js_urls
69
- extension feedback highlight_mode layout_css_urls layout_html_urls
70
- layout_js_urls multifile name output_content_type prompt queriable runner_url
71
- stateful_console test_extension test_template triable visible_success_output)).symbolize_keys
72
- end
73
-
74
94
  private
75
95
 
76
96
  # TODO we should use Mumukit::Directives::Pipeline
@@ -1,10 +1,12 @@
1
1
  class Organization < ApplicationRecord
2
- include Syncable
3
- include Mumukit::Platform::Organization::Helpers
2
+ include Mumuki::Domain::Syncable
3
+ include Mumuki::Domain::Helpers::Organization
4
4
 
5
- serialize :profile, Mumukit::Platform::Organization::Profile
6
- serialize :settings, Mumukit::Platform::Organization::Settings
7
- serialize :theme, Mumukit::Platform::Organization::Theme
5
+ include Mumukit::Login::OrganizationHelpers
6
+
7
+ serialize :profile, Mumuki::Domain::Organization::Profile
8
+ serialize :settings, Mumuki::Domain::Organization::Settings
9
+ serialize :theme, Mumuki::Domain::Organization::Theme
8
10
 
9
11
  markdown_on :description
10
12
 
@@ -16,7 +18,7 @@ class Organization < ApplicationRecord
16
18
  validates_presence_of :contact_email, :locale
17
19
  validates :name, uniqueness: true,
18
20
  presence: true,
19
- format: { with: Mumukit::Platform::Organization::Helpers.anchored_valid_name_regex }
21
+ format: { with: Mumukit::Platform::Organization.anchored_valid_name_regex }
20
22
  validates :locale, inclusion: { in: Mumukit::Platform::Locale.supported }
21
23
 
22
24
  after_create :reindex_usages!
@@ -28,6 +30,8 @@ class Organization < ApplicationRecord
28
30
  has_many :exams
29
31
  has_many :courses
30
32
 
33
+ resource_fields :name, :book, :profile, :settings, :theme
34
+
31
35
  defaults do
32
36
  self.class.base.try do |base|
33
37
  self.theme = base.theme if theme.empty?
@@ -113,11 +117,15 @@ class Organization < ApplicationRecord
113
117
  end
114
118
 
115
119
  def import_from_resource_h!(resource_h)
116
- attrs = Mumukit::Platform::Organization::Helpers.slice_resource_h resource_h
120
+ attrs = self.class.slice_resource_h resource_h
117
121
  attrs[:book] = Book.locate! attrs[:book]
118
122
  update! attrs
119
123
  end
120
124
 
125
+ def to_resource_h
126
+ super.merge(book: book.slug)
127
+ end
128
+
121
129
  private
122
130
 
123
131
  def ensure_consistent_public_login
data/app/models/user.rb CHANGED
@@ -1,10 +1,10 @@
1
1
  class User < ApplicationRecord
2
- include Syncable
2
+ include Mumuki::Domain::Syncable
3
3
  include WithProfile,
4
4
  WithUserNavigation,
5
5
  WithReminders,
6
6
  WithDiscussionCreation,
7
- Mumukit::Platform::User::Helpers
7
+ Mumuki::Domain::Helpers::User
8
8
 
9
9
  serialize :permissions, Mumukit::Auth::Permissions
10
10
 
@@ -31,9 +31,13 @@ class User < ApplicationRecord
31
31
 
32
32
  after_initialize :init
33
33
 
34
+ enum gender: %i(female male other)
35
+
34
36
  before_validation :set_uid!
35
37
  validates :uid, presence: true
36
38
 
39
+ resource_fields :uid, :social_id, :image_url, :email, :permissions, *profile_fields
40
+
37
41
  def last_lesson
38
42
  last_guide.try(:lesson)
39
43
  end
@@ -98,7 +102,7 @@ class User < ApplicationRecord
98
102
  end
99
103
 
100
104
  def import_from_resource_h!(json)
101
- update! Mumukit::Platform::User::Helpers.slice_resource_h json
105
+ update! self.class.slice_resource_h json
102
106
  end
103
107
 
104
108
  def unsubscribe_from_reminders!
@@ -5,7 +5,7 @@ class IntroduceSettingsAndThemes < ActiveRecord::Migration[4.2]
5
5
  add_column :organizations, :profile, :text, default: "{}", null: false
6
6
 
7
7
  Organization.all.each do |organization|
8
- organization.profile = Mumukit::Platform::Organization::Profile.new(
8
+ organization.profile = Mumuki::Domain::Organization::Profile.new(
9
9
  logo_url: organization[:logo_url],
10
10
  locale: organization[:locale],
11
11
  description: organization[:description],
@@ -13,12 +13,12 @@ class IntroduceSettingsAndThemes < ActiveRecord::Migration[4.2]
13
13
  terms_of_service: organization[:terms_of_service],
14
14
  community_link: organization[:community_link]
15
15
  )
16
- organization.settings = Mumukit::Platform::Organization::Settings.new(
16
+ organization.settings = Mumuki::Domain::Organization::Settings.new(
17
17
  login_methods: organization[:login_methods],
18
18
  raise_hand_enabled: organization[:raise_hand_enabled],
19
19
  public: organization[:public]
20
20
  )
21
- organization.theme = Mumukit::Platform::Organization::Theme.new(
21
+ organization.theme = Mumuki::Domain::Organization::Theme.new(
22
22
  theme_stylesheet_url: organization[:theme_stylesheet_url],
23
23
  extension_javascript_url: organization[:extension_javascript_url]
24
24
  )
@@ -0,0 +1,5 @@
1
+ class AddOrganizationToAssignment < ActiveRecord::Migration[5.1]
2
+ def change
3
+ add_reference :assignments, :organization, index: true
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ class AddLoadingFlagsToLanguage < ActiveRecord::Migration[5.1]
2
+ def change
3
+ add_column :languages, :layout_shows_loading_content, :boolean
4
+ add_column :languages, :editor_shows_loading_content, :boolean
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ class AddNewProfileFields < ActiveRecord::Migration[5.1]
2
+ def change
3
+ add_column :users, :birthdate, :date
4
+ add_column :users, :gender, :integer
5
+ end
6
+ end
@@ -0,0 +1,17 @@
1
+ #TODO move to mumukit-core
2
+ class Module
3
+ def ensure_defined!(selector)
4
+ # FIXME pass additional false flag in ruby 2.6
5
+ raise "method #{selector} was not previously defined here" unless method_defined?(selector)
6
+ end
7
+
8
+ def ensure_undefined!(selector)
9
+ # FIXME pass additional false flag in ruby 2.6
10
+ raise "method #{selector} was previously defined here" if method_defined?(selector)
11
+ end
12
+
13
+ def define_once(selector, *args, &block)
14
+ ensure_undefined! selector
15
+ define_method selector, *args, &block
16
+ end
17
+ end
@@ -1,2 +1,3 @@
1
1
  require_relative './extensions/string'
2
2
  require_relative './extensions/array'
3
+ require_relative './extensions/module'
@@ -3,5 +3,6 @@ FactoryBot.define do
3
3
  status { :pending }
4
4
  exercise
5
5
  submitter { create(:user) }
6
+ organization { Organization.current }
6
7
  end
7
8
  end
@@ -9,10 +9,8 @@ FactoryBot.define do
9
9
  book
10
10
  end
11
11
 
12
- factory :base, parent: :organization do
13
- public { true }
14
- name { 'base' }
15
- login_methods { Mumukit::Login::Settings.login_methods }
12
+ factory :private_organization, parent: :organization do
13
+ name { 'the-private-org' }
16
14
  end
17
15
 
18
16
  factory :public_organization, parent: :organization do
@@ -21,7 +19,20 @@ FactoryBot.define do
21
19
  login_methods { Mumukit::Login::Settings.login_methods }
22
20
  end
23
21
 
24
- factory :private_organization, parent: :organization do
25
- name { 'the-private-org' }
22
+ factory :base, parent: :public_organization do
23
+ name { 'base' }
24
+ end
25
+
26
+ factory :test_organization, parent: :public_organization do
27
+ name { 'test' }
28
+ book { create(:book, name: 'test', slug: 'mumuki/mumuki-the-book') }
29
+ end
30
+
31
+ factory :another_test_organization, parent: :test_organization, traits: [:skip_unique_name_validation] do
32
+ book { create(:book, name: 'another-test', slug: 'mumuki/mumuki-another-book') }
33
+ end
34
+
35
+ trait :skip_unique_name_validation do
36
+ to_create { |instance| instance.save(validate: false) }
26
37
  end
27
38
  end
@@ -4,5 +4,7 @@ FactoryBot.define do
4
4
  uid { email }
5
5
  first_name { Faker::Name.first_name }
6
6
  last_name { Faker::Name.last_name }
7
+ gender { 1 }
8
+ birthdate { Date.today }
7
9
  end
8
10
  end
@@ -0,0 +1,13 @@
1
+ module Mumuki::Domain::Helpers::Course
2
+ include Mumukit::Platform::Notifiable
3
+
4
+ def platform_class_name
5
+ :Course
6
+ end
7
+
8
+ ## API Exposure
9
+
10
+ def to_param
11
+ slug
12
+ end
13
+ end
@@ -0,0 +1,72 @@
1
+ module Mumuki::Domain::Helpers::Organization
2
+ extend ActiveSupport::Concern
3
+ include Mumukit::Platform::Notifiable
4
+
5
+ included do
6
+ delegate *Mumuki::Domain::Organization::Theme.accessors, to: :theme
7
+ delegate *Mumuki::Domain::Organization::Settings.accessors, :private?, :login_settings, to: :settings
8
+ delegate *Mumuki::Domain::Organization::Profile.accessors, :locale_json, to: :profile
9
+ end
10
+
11
+ def platform_class_name
12
+ :Organization
13
+ end
14
+
15
+ def slug
16
+ Mumukit::Auth::Slug.join_s name
17
+ end
18
+
19
+ def central?
20
+ name == 'central'
21
+ end
22
+
23
+ def test?
24
+ name == 'test'
25
+ end
26
+
27
+ def base?
28
+ name == 'base'
29
+ end
30
+
31
+ def switch!
32
+ Mumukit::Platform::Organization.switch! self
33
+ end
34
+
35
+ def to_s
36
+ name
37
+ end
38
+
39
+ def url_for(path)
40
+ Mumukit::Platform.application.organic_url_for(name, path)
41
+ end
42
+
43
+ def url
44
+ url_for '/'
45
+ end
46
+
47
+ def domain
48
+ Mumukit::Platform.application.organic_domain(name)
49
+ end
50
+
51
+ ## API Exposure
52
+
53
+ def to_param
54
+ name
55
+ end
56
+
57
+ ## Resource Hash
58
+
59
+ module ClassMethods
60
+ def current
61
+ Mumukit::Platform::Organization.current
62
+ end
63
+
64
+ def parse(json)
65
+ json
66
+ .slice(:name)
67
+ .merge(theme: Mumuki::Domain::Organization::Theme.parse(json[:theme]))
68
+ .merge(settings: Mumuki::Domain::Organization::Settings.parse(json[:settings]))
69
+ .merge(profile: Mumuki::Domain::Organization::Profile.parse(json[:profile]))
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,109 @@
1
+ module Mumuki::Domain::Helpers::User
2
+ extend ActiveSupport::Concern
3
+ include Mumukit::Auth::Roles
4
+ include Mumukit::Platform::Notifiable
5
+
6
+ ## Permissions
7
+
8
+ delegate :has_role?,
9
+ :add_permission!,
10
+ :remove_permission!,
11
+ :has_permission?,
12
+ :has_permission_delegation?,
13
+ :protect!,
14
+ :protect_delegation!,
15
+ :protect_permissions_assignment!,
16
+ to: :permissions
17
+
18
+ def platform_class_name
19
+ :User
20
+ end
21
+
22
+ def merge_permissions!(new_permissions)
23
+ self.permissions = permissions.merge(new_permissions)
24
+ end
25
+
26
+ (Mumukit::Auth::Roles::ROLES - [:writer, :editor, :owner] + [:discusser]).each do |role|
27
+ role_of = "#{role}_of?"
28
+ role_here = "#{role}_here?"
29
+
30
+ # Tells whether this user has #{role} permissions in
31
+ # the given organization
32
+ define_method role_of do |organization|
33
+ has_permission? role, organization.slug
34
+ end
35
+
36
+ # Tells whether this user has #{role} permissions in
37
+ # the current organization
38
+ define_method role_here do
39
+ send role_of, Mumukit::Platform::Organization.current
40
+ end
41
+ end
42
+
43
+ # Tells whether this user has forum discusser permissions in
44
+ # the given organization
45
+ def discusser_of?(organization)
46
+ has_permission? organization.forum_discussions_minimal_role, organization.slug
47
+ end
48
+
49
+ (Mumukit::Auth::Roles::ROLES - [:owner]).each do |role|
50
+
51
+ # Assignes the #{role} role to this user
52
+ # for the given slug
53
+ define_method "make_#{role}_of!" do |slug|
54
+ add_permission! role, slug
55
+ end
56
+ end
57
+
58
+ ## Profile
59
+
60
+ def full_name
61
+ "#{first_name} #{last_name}"
62
+ end
63
+
64
+ alias_method :name, :full_name
65
+
66
+ def profile_completed?
67
+ self.class.profile_fields.map { |it| self[it] }.all? &:present?
68
+ end
69
+
70
+ def to_s
71
+ "#{full_name} <#{email}> [#{uid}]"
72
+ end
73
+
74
+ ## Accesible organizations
75
+
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
80
+ end
81
+
82
+ def has_student_granted_organizations?
83
+ student_granted_organizations.present?
84
+ end
85
+
86
+ def main_organization
87
+ student_granted_organizations.first
88
+ end
89
+
90
+ def has_main_organization?
91
+ student_granted_organizations.length == 1
92
+ end
93
+
94
+ def has_immersive_main_organization?
95
+ !!main_organization.try(&:immersive?)
96
+ end
97
+
98
+ ## API Exposure
99
+
100
+ def to_param
101
+ uid
102
+ end
103
+
104
+ class_methods do
105
+ def profile_fields
106
+ [:first_name, :last_name, :gender, :birthdate]
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,8 @@
1
+ module Mumuki::Domain
2
+ module Helpers
3
+ end
4
+ end
5
+
6
+ require_relative './helpers/organization'
7
+ require_relative './helpers/user'
8
+ require_relative './helpers/course'
@@ -0,0 +1,39 @@
1
+ class Mumuki::Domain::Organization::Profile < Mumukit::Platform::Model
2
+ LOCALES = Mumukit::Platform::Locale::SPECS
3
+
4
+ model_attr_accessor :logo_url,
5
+ :banner_url,
6
+ :favicon_url,
7
+ :breadcrumb_image_url,
8
+ :open_graph_image_url,
9
+ :locale,
10
+ :description,
11
+ :contact_email,
12
+ :terms_of_service,
13
+ :community_link,
14
+ :errors_explanations
15
+
16
+ def locale_json
17
+ locale_h.to_json
18
+ end
19
+
20
+ def locale_h
21
+ Mumukit::Platform::Locale::SPECS[locale]
22
+ end
23
+
24
+ def logo_url
25
+ @logo_url ||= 'https://mumuki.io/logo-alt-large.png' # Best image size: 350x75
26
+ end
27
+
28
+ def banner_url
29
+ @banner_url || logo_url # Best image size: 350x75
30
+ end
31
+
32
+ def favicon_url
33
+ @favicon_url ||= '/favicon.ico' # Best image size: 16x16, 32x32 or 48x48
34
+ end
35
+
36
+ def open_graph_image_url
37
+ @open_graph_image_url ||= Mumukit::Platform.application.url_for("logo-alt.png") # Best image size: 256x256
38
+ end
39
+ end
@@ -0,0 +1,27 @@
1
+ class Mumuki::Domain::Organization::Settings < Mumukit::Platform::Model
2
+ include Mumukit::Login::LoginSettingsHelpers
3
+
4
+ model_attr_accessor :login_methods,
5
+ :login_provider,
6
+ :login_provider_settings,
7
+ :forum_discussions_minimal_role,
8
+ :raise_hand_enabled?,
9
+ :feedback_suggestions_enabled?,
10
+ :public?,
11
+ :embeddable?,
12
+ :immersive?,
13
+ :forum_enabled?,
14
+ :report_issue_enabled?
15
+
16
+ def private?
17
+ !public?
18
+ end
19
+
20
+ def login_methods
21
+ @login_methods ||= ['user_pass']
22
+ end
23
+
24
+ def forum_discussions_minimal_role
25
+ (@forum_discussions_minimal_role || 'student').to_sym
26
+ end
27
+ end
@@ -0,0 +1,4 @@
1
+ class Mumuki::Domain::Organization::Theme < Mumukit::Platform::Model
2
+ model_attr_accessor :theme_stylesheet,
3
+ :extension_javascript
4
+ end
@@ -0,0 +1,8 @@
1
+ module Mumuki::Domain
2
+ module Organization
3
+ end
4
+ end
5
+
6
+ require_relative './organization/settings'
7
+ require_relative './organization/profile'
8
+ require_relative './organization/theme'
@@ -0,0 +1,29 @@
1
+ module Mumukit::Sync::Store
2
+
3
+ ## This Store enables importing content
4
+ ## from Bibliotheca API
5
+ class Bibliotheca < Mumukit::Sync::Store::Base
6
+ include Mumukit::Sync::Store::WithWrappedLanguage
7
+ include Mumukit::Sync::Store::WithFilteredId
8
+
9
+ def initialize(bibliotheca_bridge)
10
+ @bibliotheca_bridge = bibliotheca_bridge
11
+ end
12
+
13
+ def sync_keys
14
+ %w(guide topic book).flat_map do |kind|
15
+ @bibliotheca_bridge
16
+ .send(kind.as_variable_name.pluralize)
17
+ .map { |it| Mumukit::Sync.key kind, it['slug'] }
18
+ end
19
+ end
20
+
21
+ def do_read(sync_key)
22
+ @bibliotheca_bridge.send(sync_key.kind.as_variable_name, sync_key.id)
23
+ end
24
+
25
+ def write_resource!(*)
26
+ Mumukit::Sync::Store.read_only!
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,37 @@
1
+ module Mumukit::Sync::Store::Github::Schema::Exercise
2
+ extend Mumukit::Sync::Store::Github::Schema
3
+
4
+ def self.fields_schema
5
+ [
6
+ {name: :id, kind: :special},
7
+ {name: :name, kind: :special},
8
+
9
+ {name: :tags, kind: :metadata, reverse: :tag_list, transform: with { |it| it.to_a }},
10
+ {name: :layout, kind: :metadata},
11
+ {name: :editor, kind: :metadata},
12
+
13
+ {name: :type, kind: :metadata},
14
+ {name: :extra_visible, kind: :metadata},
15
+ {name: :language, kind: :metadata, transform: name },
16
+ {name: :teacher_info, kind: :metadata},
17
+ {name: :manual_evaluation, kind: :metadata},
18
+ {name: :choices, kind: :metadata},
19
+
20
+ {name: :expectations, kind: :file, extension: 'yml', transform: yaml_list('expectations')},
21
+ {name: :assistance_rules, kind: :file, extension: 'yml', transform: yaml_list('rules')},
22
+ {name: :randomizations, kind: :file, extension: 'yml', transform: yaml_hash},
23
+
24
+ {name: :goal, kind: :metadata},
25
+ {name: :test, kind: :file, extension: :test},
26
+ {name: :extra, kind: :file, extension: :code},
27
+ {name: :default, kind: :file, extension: :code, reverse: :default_content},
28
+
29
+ {name: :description, kind: :file, extension: 'md', required: true},
30
+ {name: :hint, kind: :file, extension: 'md'},
31
+ {name: :corollary, kind: :file, extension: 'md'},
32
+ {name: :initial_state, kind: :file, extension: 'md'},
33
+ {name: :final_state, kind: :file, extension: 'md'},
34
+ {name: :free_form_editor_source, kind: :file, extension: 'html'}
35
+ ]
36
+ end
37
+ end
@@ -0,0 +1,34 @@
1
+ module Mumukit::Sync::Store::Github::Schema::Guide
2
+ extend Mumukit::Sync::Store::Github::Schema
3
+
4
+ def self.fields_schema
5
+ [
6
+ {name: :exercises, kind: :special},
7
+ {name: :id, kind: :special},
8
+ {name: :slug, kind: :special},
9
+
10
+ {name: :name, kind: :metadata},
11
+ {name: :locale, kind: :metadata},
12
+ {name: :type, kind: :metadata},
13
+ {name: :beta, kind: :metadata},
14
+ {name: :teacher_info, kind: :metadata},
15
+ {name: :language, kind: :metadata, transform: name },
16
+ {name: :id_format, kind: :metadata},
17
+ {name: :order, kind: :metadata, transform: with { |it| it.map { |e| e[:id] } }, reverse: :exercises},
18
+ {name: :private, kind: :metadata},
19
+
20
+ {name: :expectations, kind: :file, extension: 'yml', transform: yaml_list('expectations')},
21
+ {name: :description, kind: :file, extension: 'md', required: true},
22
+ {name: :corollary, kind: :file, extension: 'md'},
23
+ {name: :sources, kind: :file, extension: 'md'},
24
+ {name: :learn_more, kind: :file, extension: 'md'},
25
+ {name: :extra, kind: :file, extension: :code},
26
+ {name: :AUTHORS, kind: :file, extension: 'txt', reverse: :authors},
27
+ {name: :COLLABORATORS, kind: :file, extension: 'txt', reverse: :collaborators}
28
+ ]
29
+ end
30
+
31
+ def self.fixed_file_patterns
32
+ %w(LICENSE.txt README.md COPYRIGHT.txt meta.yml *_*/*)
33
+ end
34
+ end
@@ -0,0 +1,79 @@
1
+ module Mumukit::Sync::Store
2
+
3
+ ## This Store enables importing languages
4
+ ## from Thesaurus API
5
+ class Thesaurus < Mumukit::Sync::Store::Base
6
+ def initialize(thesaurus_bridge)
7
+ @thesaurus_bridge = thesaurus_bridge
8
+ end
9
+
10
+ def sync_keys
11
+ @thesaurus_bridge.runners.map { |it| Mumukit::Sync.key(:language, it) }
12
+ end
13
+
14
+ def do_read(sync_key)
15
+ return unless sync_key.kind.like? :language
16
+ transform_after_read(sync_key.id, Mumukit::Bridge::Runner.new(sync_key.id).info)
17
+ end
18
+
19
+ def transform_after_read(runner_url, info)
20
+ Mumukit::Sync::Store::Thesaurus::InfoConverter.new(runner_url, info).call
21
+ end
22
+
23
+ def write_resource!(*)
24
+ Mumukit::Sync::Store.read_only!
25
+ end
26
+
27
+ class InfoConverter
28
+ def initialize(runner_url, info)
29
+ @runner_url = runner_url
30
+ @info = info
31
+ end
32
+
33
+ def call
34
+ {
35
+ name: @info['name'],
36
+ comment_type: @info['comment_type'],
37
+ runner_url: @runner_url,
38
+ output_content_type: @info['output_content_type'],
39
+ prompt: (@info.dig('language', 'prompt') || 'ム') + ' ',
40
+ extension: @info.dig('language', 'extension'),
41
+ highlight_mode: @info.dig('language', 'ace_mode'),
42
+ visible_success_output: @info.dig('language', 'graphic').present?,
43
+ devicon: @info.dig('language', 'icon', 'name'),
44
+ triable: @info.dig('features', 'try').present?,
45
+ feedback: @info.dig('features', 'feedback').present?,
46
+ queriable: @info.dig('features', 'query').present?,
47
+ stateful_console: @info.dig('features', 'stateful').present?,
48
+ multifile: @info.dig('features', 'multifile').present?,
49
+ test_extension: @info.dig('test_framework', 'test_extension'),
50
+ test_template: @info.dig('test_framework', 'template'),
51
+ layout_js_urls: get_assets_for(:layout, 'js'),
52
+ layout_html_urls: get_assets_for(:layout, 'html'),
53
+ layout_css_urls: get_assets_for(:layout, 'css'),
54
+ editor_js_urls: get_assets_for(:editor, 'js'),
55
+ editor_html_urls: get_assets_for(:editor, 'html'),
56
+ editor_css_urls: get_assets_for(:editor, 'css'),
57
+ layout_shows_loading_content: shows_loading_content_for?(:layout),
58
+ editor_shows_loading_content: shows_loading_content_for?(:editor)
59
+ }
60
+ end
61
+
62
+ def get_assets_for(kind, content_type)
63
+ absolutize(get_asset_field(kind, content_type) || [])
64
+ end
65
+
66
+ def get_asset_field(kind, field)
67
+ @info.dig("#{kind}_assets_urls", field)
68
+ end
69
+
70
+ def absolutize(urls)
71
+ urls.map { |url| "#{@runner_url}/#{url}"}
72
+ end
73
+
74
+ def shows_loading_content_for?(kind)
75
+ get_asset_field(kind, 'shows_loading_content').present?
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,4 @@
1
+ require_relative './stores/thesaurus_store'
2
+ require_relative './stores/bibliotheca_store'
3
+ require_relative './stores/guide_schema'
4
+ require_relative './stores/exercise_schema'
@@ -0,0 +1,18 @@
1
+ module Mumuki::Domain::Syncable
2
+ ## Syncable objects that also declare
3
+ ## a `resource_fields` method can define `resource_h`
4
+ ## and `self.slice_resource_h` based on it.
5
+ module WithResourceFields
6
+ extend ActiveSupport::Concern
7
+
8
+ def to_resource_h
9
+ self.class.resource_fields.map { |it| [it, send(it)] }.to_h.compact
10
+ end
11
+
12
+ class_methods do
13
+ def slice_resource_h(resource_h)
14
+ resource_h.slice(*resource_fields)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -2,7 +2,7 @@
2
2
  # be treated as `Mumukit::Sync` resources
3
3
  #
4
4
  # `Syncable` objects also get `Mumukit::Platform::Notifiable` by free
5
- module Syncable
5
+ module Mumuki::Domain::Syncable
6
6
  extend ActiveSupport::Concern
7
7
  include Mumukit::Platform::Notifiable
8
8
 
@@ -87,3 +87,5 @@ module Syncable
87
87
  end
88
88
  end
89
89
  end
90
+
91
+ require_relative './syncable/with_resource_fields'
@@ -1,5 +1,5 @@
1
1
  module Mumuki
2
2
  module Domain
3
- VERSION = '6.7.2'
3
+ VERSION = '7.0.0'
4
4
  end
5
5
  end
data/lib/mumuki/domain.rb CHANGED
@@ -11,6 +11,7 @@ require 'mumukit/inspection'
11
11
  require 'mumukit/platform'
12
12
  require 'mumukit/randomizer'
13
13
  require 'mumukit/sync'
14
+ require 'mumukit/login'
14
15
 
15
16
  I18n.load_translations_path File.join(__dir__, 'domain', 'locales', '**', '*.yml')
16
17
 
@@ -30,6 +31,10 @@ require_relative './domain/status'
30
31
  require_relative './domain/exceptions'
31
32
  require_relative './domain/file'
32
33
  require_relative './domain/extensions'
34
+ require_relative './domain/organization'
35
+ require_relative './domain/helpers'
36
+ require_relative './domain/syncable'
37
+ require_relative './domain/stores'
33
38
 
34
39
  class Mumukit::Assistant
35
40
  def self.valid?(rules)
@@ -48,3 +53,8 @@ class Mumukit::Randomizer
48
53
  !!parse(randomizations) rescue false
49
54
  end
50
55
  end
56
+
57
+ Mumukit::Sync::Store::Github.configure do |config|
58
+ config.guide_schema = Mumukit::Sync::Store::Github::Schema::Guide
59
+ config.exercise_schema = Mumukit::Sync::Store::Github::Schema::Exercise
60
+ 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: 6.7.2
4
+ version: 7.0.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: 2019-05-28 00:00:00.000000000 Z
11
+ date: 2019-07-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '3.8'
61
+ version: '4.0'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '3.8'
68
+ version: '4.0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: mumukit-content-type
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -142,28 +142,28 @@ dependencies:
142
142
  requirements:
143
143
  - - "~>"
144
144
  - !ruby/object:Gem::Version
145
- version: '4.2'
145
+ version: '5.1'
146
146
  type: :runtime
147
147
  prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
150
  - - "~>"
151
151
  - !ruby/object:Gem::Version
152
- version: '4.2'
152
+ version: '5.1'
153
153
  - !ruby/object:Gem::Dependency
154
154
  name: mumukit-sync
155
155
  requirement: !ruby/object:Gem::Requirement
156
156
  requirements:
157
157
  - - "~>"
158
158
  - !ruby/object:Gem::Version
159
- version: '0.3'
159
+ version: '1.0'
160
160
  type: :runtime
161
161
  prerelease: false
162
162
  version_requirements: !ruby/object:Gem::Requirement
163
163
  requirements:
164
164
  - - "~>"
165
165
  - !ruby/object:Gem::Version
166
- version: '0.3'
166
+ version: '1.0'
167
167
  - !ruby/object:Gem::Dependency
168
168
  name: pg
169
169
  requirement: !ruby/object:Gem::Requirement
@@ -184,14 +184,14 @@ dependencies:
184
184
  requirements:
185
185
  - - "~>"
186
186
  - !ruby/object:Gem::Version
187
- version: '6.1'
187
+ version: '7.0'
188
188
  type: :development
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: '6.1'
194
+ version: '7.0'
195
195
  description: Mumuki Platform's Domain Model
196
196
  email:
197
197
  - franco@mumuki.org
@@ -221,7 +221,6 @@ files:
221
221
  - app/models/concerns/submittable/solvable.rb
222
222
  - app/models/concerns/submittable/submittable.rb
223
223
  - app/models/concerns/submittable/triable.rb
224
- - app/models/concerns/syncable.rb
225
224
  - app/models/concerns/with_assignments.rb
226
225
  - app/models/concerns/with_case_insensitive_search.rb
227
226
  - app/models/concerns/with_description.rb
@@ -538,6 +537,9 @@ files:
538
537
  - db/migrate/20190326152631_add_settings_to_content.rb
539
538
  - db/migrate/20190404181724_add_organization_to_discussion.rb
540
539
  - db/migrate/20190506180102_add_multifile_to_languages.rb
540
+ - db/migrate/20190530173142_add_organization_to_assignment.rb
541
+ - db/migrate/20190702003600_add_loading_flags_to_language.rb
542
+ - db/migrate/20190702182407_add_new_profile_fields.rb
541
543
  - lib/mumuki/domain.rb
542
544
  - lib/mumuki/domain/engine.rb
543
545
  - lib/mumuki/domain/evaluation.rb
@@ -551,6 +553,7 @@ files:
551
553
  - lib/mumuki/domain/exceptions/unauthorized_error.rb
552
554
  - lib/mumuki/domain/extensions.rb
553
555
  - lib/mumuki/domain/extensions/array.rb
556
+ - lib/mumuki/domain/extensions/module.rb
554
557
  - lib/mumuki/domain/extensions/string.rb
555
558
  - lib/mumuki/domain/factories.rb
556
559
  - lib/mumuki/domain/factories/api_client_factory.rb
@@ -572,12 +575,20 @@ files:
572
575
  - lib/mumuki/domain/factories/usage_factory.rb
573
576
  - lib/mumuki/domain/factories/user_factory.rb
574
577
  - lib/mumuki/domain/file.rb
578
+ - lib/mumuki/domain/helpers.rb
579
+ - lib/mumuki/domain/helpers/course.rb
580
+ - lib/mumuki/domain/helpers/organization.rb
581
+ - lib/mumuki/domain/helpers/user.rb
575
582
  - lib/mumuki/domain/locales/activerecord/en.yml
576
583
  - lib/mumuki/domain/locales/activerecord/es.yml
577
584
  - lib/mumuki/domain/locales/activerecord/pt.yml
578
585
  - lib/mumuki/domain/locales/console_submission/en.yml
579
586
  - lib/mumuki/domain/locales/console_submission/es.yml
580
587
  - lib/mumuki/domain/locales/console_submission/pt.yml
588
+ - lib/mumuki/domain/organization.rb
589
+ - lib/mumuki/domain/organization/profile.rb
590
+ - lib/mumuki/domain/organization/settings.rb
591
+ - lib/mumuki/domain/organization/theme.rb
581
592
  - lib/mumuki/domain/seed.rb
582
593
  - lib/mumuki/domain/status.rb
583
594
  - lib/mumuki/domain/status/discussion/closed.rb
@@ -594,6 +605,11 @@ files:
594
605
  - lib/mumuki/domain/status/submission/pending.rb
595
606
  - lib/mumuki/domain/status/submission/running.rb
596
607
  - lib/mumuki/domain/status/submission/submission.rb
608
+ - lib/mumuki/domain/stores.rb
609
+ - lib/mumuki/domain/stores/bibliotheca_store.rb
610
+ - lib/mumuki/domain/stores/exercise_schema.rb
611
+ - lib/mumuki/domain/stores/guide_schema.rb
612
+ - lib/mumuki/domain/stores/thesaurus_store.rb
597
613
  - lib/mumuki/domain/submission.rb
598
614
  - lib/mumuki/domain/submission/base.rb
599
615
  - lib/mumuki/domain/submission/confirmation.rb
@@ -603,6 +619,8 @@ files:
603
619
  - lib/mumuki/domain/submission/question.rb
604
620
  - lib/mumuki/domain/submission/solution.rb
605
621
  - lib/mumuki/domain/submission/try.rb
622
+ - lib/mumuki/domain/syncable.rb
623
+ - lib/mumuki/domain/syncable/with_resource_fields.rb
606
624
  - lib/mumuki/domain/version.rb
607
625
  homepage: https://mumuki.org
608
626
  licenses:
@@ -623,7 +641,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
623
641
  - !ruby/object:Gem::Version
624
642
  version: '0'
625
643
  requirements: []
626
- rubygems_version: 3.0.3
644
+ rubyforge_project:
645
+ rubygems_version: 2.7.7
627
646
  signing_key:
628
647
  specification_version: 4
629
648
  summary: Mumuki Platform's Domain Model