mumuki-domain 6.7.2 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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