meibo 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +13 -0
  4. data/CHANGELOG.md +5 -0
  5. data/CODE_OF_CONDUCT.md +84 -0
  6. data/Gemfile +16 -0
  7. data/Gemfile.lock +84 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +41 -0
  10. data/Rakefile +12 -0
  11. data/lib/meibo/academic_session.rb +48 -0
  12. data/lib/meibo/academic_session_set.rb +15 -0
  13. data/lib/meibo/base_profile.rb +37 -0
  14. data/lib/meibo/builder/academic_session_builder.rb +26 -0
  15. data/lib/meibo/builder/base_builder.rb +13 -0
  16. data/lib/meibo/builder/classroom_builder.rb +38 -0
  17. data/lib/meibo/builder/course_builder.rb +32 -0
  18. data/lib/meibo/builder/demographic_builder.rb +22 -0
  19. data/lib/meibo/builder/enrollment_builder.rb +30 -0
  20. data/lib/meibo/builder/organization_builder.rb +34 -0
  21. data/lib/meibo/builder/role_builder.rb +30 -0
  22. data/lib/meibo/builder/user_builder.rb +36 -0
  23. data/lib/meibo/builder/user_profile_builder.rb +22 -0
  24. data/lib/meibo/builder.rb +60 -0
  25. data/lib/meibo/classroom.rb +67 -0
  26. data/lib/meibo/classroom_set.rb +29 -0
  27. data/lib/meibo/converter.rb +259 -0
  28. data/lib/meibo/course.rb +45 -0
  29. data/lib/meibo/course_set.rb +23 -0
  30. data/lib/meibo/data_model.rb +84 -0
  31. data/lib/meibo/data_set.rb +44 -0
  32. data/lib/meibo/demographic.rb +70 -0
  33. data/lib/meibo/demographic_set.rb +18 -0
  34. data/lib/meibo/enrollment.rb +51 -0
  35. data/lib/meibo/enrollment_set.rb +22 -0
  36. data/lib/meibo/errors.rb +13 -0
  37. data/lib/meibo/factory_bot/academic_session.rb +23 -0
  38. data/lib/meibo/factory_bot/all.rb +13 -0
  39. data/lib/meibo/factory_bot/classroom.rb +28 -0
  40. data/lib/meibo/factory_bot/course.rb +21 -0
  41. data/lib/meibo/factory_bot/demographic.rb +16 -0
  42. data/lib/meibo/factory_bot/enrollment.rb +37 -0
  43. data/lib/meibo/factory_bot/manifest.rb +33 -0
  44. data/lib/meibo/factory_bot/memory_package.rb +11 -0
  45. data/lib/meibo/factory_bot/organization.rb +47 -0
  46. data/lib/meibo/factory_bot/role.rb +41 -0
  47. data/lib/meibo/factory_bot/user.rb +22 -0
  48. data/lib/meibo/factory_bot/user_profile.rb +21 -0
  49. data/lib/meibo/japan_profile/academic_session.rb +26 -0
  50. data/lib/meibo/japan_profile/classroom.rb +21 -0
  51. data/lib/meibo/japan_profile/course.rb +18 -0
  52. data/lib/meibo/japan_profile/demographic.rb +15 -0
  53. data/lib/meibo/japan_profile/enrollment.rb +35 -0
  54. data/lib/meibo/japan_profile/organization.rb +20 -0
  55. data/lib/meibo/japan_profile/role.rb +26 -0
  56. data/lib/meibo/japan_profile/user.rb +27 -0
  57. data/lib/meibo/japan_profile/user_profile.rb +7 -0
  58. data/lib/meibo/japan_profile.rb +39 -0
  59. data/lib/meibo/manifest/processing_mode.rb +40 -0
  60. data/lib/meibo/manifest.rb +163 -0
  61. data/lib/meibo/organization.rb +44 -0
  62. data/lib/meibo/organization_set.rb +15 -0
  63. data/lib/meibo/reader.rb +138 -0
  64. data/lib/meibo/role.rb +65 -0
  65. data/lib/meibo/role_set.rb +25 -0
  66. data/lib/meibo/roster.rb +166 -0
  67. data/lib/meibo/user.rb +71 -0
  68. data/lib/meibo/user_profile.rb +42 -0
  69. data/lib/meibo/user_profile_set.rb +18 -0
  70. data/lib/meibo/user_set.rb +24 -0
  71. data/lib/meibo/version.rb +5 -0
  72. data/lib/meibo.rb +17 -0
  73. data/meibo.gemspec +38 -0
  74. data/sig/meibo.rbs +4 -0
  75. metadata +148 -0
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Meibo
4
+ module JapanProfile
5
+ class User < ::Meibo::User
6
+ DataModel.define(
7
+ self,
8
+ attribute_name_to_header_field_map: superclass.attribute_name_to_header_field_map.merge(
9
+ kana_given_name: 'metadata.jp.kanaGivenName',
10
+ kana_family_name: 'metadata.jp.kanaFamilyName',
11
+ kana_middle_name: 'metadata.jp.kanaMiddleName',
12
+ home_class: 'metadata.jp.homeClass'
13
+ ).freeze,
14
+ converters: superclass.converters
15
+ )
16
+
17
+ # NOTE: enabled_userは必須ではないが固定
18
+ def initialize(enabled_user: true, kana_given_name: nil, kana_family_name: nil, kana_middle_name: nil, home_class: nil, **other_fields)
19
+ super(enabled_user: enabled_user, **other_fields)
20
+ @kana_given_name = kana_given_name
21
+ @kana_family_name = kana_family_name
22
+ @kana_middle_name = kana_middle_name
23
+ @home_class = home_class
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Meibo
4
+ module JapanProfile
5
+ UserProfile = ::Meibo::UserProfile
6
+ end
7
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Meibo
4
+ module JapanProfile
5
+ include BaseProfile
6
+
7
+ BUILDERS = {
8
+ academic_session: Builder::AcademicSessionBuilder.create(AcademicSession),
9
+ class: Builder::ClassroomBuilder.create(Classroom),
10
+ course: Builder::CourseBuilder.create(Course),
11
+ demographic: Builder::DemographicBuilder.create(Demographic),
12
+ enrollment: Builder::EnrollmentBuilder.create(Enrollment),
13
+ org: Builder::OrganizationBuilder.create(Organization),
14
+ role: Builder::RoleBuilder.create(Role),
15
+ user: Builder::UserBuilder.create(User),
16
+ user_profile: Builder::UserProfileBuilder.create(UserProfile)
17
+ }
18
+
19
+ DATA_MODELS = {
20
+ file_academic_sessions: Meibo::JapanProfile::AcademicSession,
21
+ file_classes: Meibo::JapanProfile::Classroom,
22
+ file_courses: Meibo::JapanProfile::Course,
23
+ file_demographics: Meibo::JapanProfile::Demographic,
24
+ file_enrollments: Meibo::JapanProfile::Enrollment,
25
+ file_orgs: Meibo::JapanProfile::Organization,
26
+ file_roles: Meibo::JapanProfile::Role,
27
+ file_user_profiles: Meibo::JapanProfile::UserProfile,
28
+ file_users: Meibo::JapanProfile::User
29
+ }
30
+
31
+ def self.builder_for(key)
32
+ BUILDERS[key]
33
+ end
34
+
35
+ def self.data_model_for(file_attribute)
36
+ DATA_MODELS[file_attribute]
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Meibo
4
+ class Manifest
5
+ class ProcessingMode
6
+ attr_reader :mode
7
+
8
+ def initialize(mode)
9
+ @mode = mode
10
+ freeze
11
+ end
12
+
13
+ def absent?
14
+ @mode == 'absent'
15
+ end
16
+
17
+ def bulk?
18
+ @mode == 'bulk'
19
+ end
20
+
21
+ def delta?
22
+ @mode == 'delta'
23
+ end
24
+
25
+ def ==(other)
26
+ if other.is_a?(ProcessingMode)
27
+ @mode == other.mode
28
+ elsif other.is_a?(String)
29
+ to_s == other
30
+ else
31
+ false
32
+ end
33
+ end
34
+
35
+ def to_s
36
+ @mode
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Meibo
4
+ class Manifest
5
+ MANIFEST_VERSION = '1.0'
6
+ ONEROSTER_VERSION = '1.2'
7
+
8
+ PROCESSING_MODES = {
9
+ absent: ProcessingMode.new('absent'),
10
+ bulk: ProcessingMode.new('bulk'),
11
+ delta: ProcessingMode.new('delta')
12
+ }.freeze
13
+
14
+ PROPERTY_NAME_TO_ATTRIBUTE_MAP = {
15
+ 'manifest.version' => :manifest_version,
16
+ 'oneroster.version' => :oneroster_version,
17
+ 'file.academicSessions' => :file_academic_sessions,
18
+ 'file.categories' => :file_categories,
19
+ 'file.classes' => :file_classes,
20
+ 'file.classResources' => :file_class_resources,
21
+ 'file.courses' => :file_courses,
22
+ 'file.courseResources' => :file_course_resources,
23
+ 'file.demographics' => :file_demographics,
24
+ 'file.enrollments' => :file_enrollments,
25
+ 'file.lineItemLearningObjectiveIds' => :file_line_item_learning_objective_ids,
26
+ 'file.lineItems' => :file_line_items,
27
+ 'file.lineItemScoreScales' => :file_line_item_score_scales,
28
+ 'file.orgs' => :file_orgs,
29
+ 'file.resources' => :file_resources,
30
+ 'file.resultLearningObjectiveIds' => :file_result_learning_objective_ids,
31
+ 'file.results' => :file_results,
32
+ 'file.resultScoreScales' => :file_result_score_scales,
33
+ 'file.roles' => :file_roles,
34
+ 'file.scoreScales' => :file_score_scales,
35
+ 'file.userProfiles' => :file_user_profiles,
36
+ 'file.userResources' => :file_user_resources,
37
+ 'file.users' => :file_users,
38
+ 'source.systemName' => :source_system_name,
39
+ 'source.systemCode' => :source_system_code
40
+ }.freeze
41
+ ATTRIBUTE_TO_PROPERTY_NAME_MAP = PROPERTY_NAME_TO_ATTRIBUTE_MAP.to_h {|property_name, attribute| [attribute, property_name] }.freeze
42
+
43
+ # NOTE: 想定値
44
+ DEFAULT_VALUES = {
45
+ manifest_version: MANIFEST_VERSION,
46
+ oneroster_version: ONEROSTER_VERSION,
47
+ file_academic_sessions: PROCESSING_MODES[:bulk],
48
+ file_categories: PROCESSING_MODES[:absent],
49
+ file_classes: PROCESSING_MODES[:bulk],
50
+ file_class_resources: PROCESSING_MODES[:absent],
51
+ file_courses: PROCESSING_MODES[:bulk],
52
+ file_course_resources: PROCESSING_MODES[:absent],
53
+ file_demographics: PROCESSING_MODES[:bulk],
54
+ file_enrollments: PROCESSING_MODES[:bulk],
55
+ file_line_item_learning_objective_ids: PROCESSING_MODES[:absent],
56
+ file_line_items: PROCESSING_MODES[:absent],
57
+ file_line_item_score_scales: PROCESSING_MODES[:absent],
58
+ file_orgs: PROCESSING_MODES[:bulk],
59
+ file_resources: PROCESSING_MODES[:absent],
60
+ file_result_learning_objective_ids: PROCESSING_MODES[:absent],
61
+ file_results: PROCESSING_MODES[:absent],
62
+ file_result_score_scales: PROCESSING_MODES[:absent],
63
+ file_roles: PROCESSING_MODES[:bulk],
64
+ file_score_scales: PROCESSING_MODES[:absent],
65
+ file_user_profiles: PROCESSING_MODES[:bulk],
66
+ file_user_resources: PROCESSING_MODES[:absent],
67
+ file_users: PROCESSING_MODES[:bulk]
68
+ }.freeze
69
+
70
+ attr_reader :manifest_version, :oneroster_version, :file_academic_sessions, :file_categories, :file_classes, :file_class_resources, :file_courses, :file_course_resources, :file_demographics, :file_enrollments, :file_line_item_learning_objective_ids, :file_line_items, :file_line_item_score_scales, :file_orgs, :file_resources, :file_result_learning_objective_ids, :file_results, :file_result_score_scales, :file_roles, :file_score_scales, :file_user_profiles, :file_user_resources, :file_users, :source_system_name, :source_system_code
71
+
72
+ def self.build_from_default(**custom_properties)
73
+ new(**Manifest::DEFAULT_VALUES.merge(custom_properties))
74
+ end
75
+
76
+ def self.filename
77
+ 'manifest.csv'
78
+ end
79
+
80
+ attribute_name_to_header_field_map = {
81
+ property_name: 'propertyName',
82
+ value: 'value'
83
+ }
84
+ header_fields = attribute_name_to_header_field_map.values
85
+ define_singleton_method(:header_fields) { header_fields }
86
+ DataModel.define_header_converters(self, attribute_name_to_header_field_map)
87
+
88
+ def self.attribute_to_property_name(attribute)
89
+ ATTRIBUTE_TO_PROPERTY_NAME_MAP.fetch(attribute)
90
+ end
91
+
92
+ def self.filename_for(attribute)
93
+ property_name = attribute_to_property_name(attribute)
94
+
95
+ raise Meibo::Error, "#{property_name}はファイルのプロパティではありません" unless property_name.start_with?('file.')
96
+
97
+ property_name.split('file.', 2).last + '.csv'
98
+ end
99
+
100
+ def self.parse(csv)
101
+ properties = CSV.parse(csv, encoding: Meibo::CSV_ENCODING, headers: true, header_converters: header_converters)
102
+ new(**properties.to_h {|property| [PROPERTY_NAME_TO_ATTRIBUTE_MAP.fetch(property[:property_name]), property[:value]] })
103
+ end
104
+
105
+ def initialize(manifest_version:, oneroster_version:, file_academic_sessions:, file_categories:, file_classes:, file_class_resources:, file_courses:, file_course_resources:, file_demographics:, file_enrollments:, file_line_item_learning_objective_ids:, file_line_items:, file_line_item_score_scales:, file_orgs:, file_resources:, file_result_learning_objective_ids:, file_results:, file_result_score_scales:, file_roles:, file_score_scales:, file_user_profiles:, file_user_resources:, file_users:, source_system_name: nil, source_system_code: nil)
106
+ @manifest_version = manifest_version
107
+ @oneroster_version = oneroster_version
108
+ @file_academic_sessions = file_academic_sessions
109
+ @file_categories = file_categories
110
+ @file_classes = file_classes
111
+ @file_class_resources = file_class_resources
112
+ @file_courses = file_courses
113
+ @file_course_resources = file_course_resources
114
+ @file_demographics = file_demographics
115
+ @file_enrollments = file_enrollments
116
+ @file_line_item_learning_objective_ids = file_line_item_learning_objective_ids
117
+ @file_line_items = file_line_items
118
+ @file_line_item_score_scales = file_line_item_score_scales
119
+ @file_orgs = file_orgs
120
+ @file_resources = file_resources
121
+ @file_result_learning_objective_ids = file_result_learning_objective_ids
122
+ @file_results = file_results
123
+ @file_result_score_scales = file_result_score_scales
124
+ @file_roles = file_roles
125
+ @file_score_scales = file_score_scales
126
+ @file_user_profiles = file_user_profiles
127
+ @file_user_resources = file_user_resources
128
+ @file_users = file_users
129
+ @source_system_name = source_system_name
130
+ @source_system_code = source_system_code
131
+ end
132
+
133
+ def filenames(processing_mode:)
134
+ file_attributes(processing_mode: processing_mode).map do |attribute|
135
+ self.class.filename_for(attribute)
136
+ end
137
+ end
138
+
139
+ def file_attributes(processing_mode:)
140
+ PROPERTY_NAME_TO_ATTRIBUTE_MAP.values.filter_map do |attribute|
141
+ attribute.start_with?('file_') && public_send(attribute) == processing_mode.to_s && attribute
142
+ end
143
+ end
144
+
145
+ def deconstruct
146
+ to_a
147
+ end
148
+
149
+ def deconstruct_keys(_keys)
150
+ to_h
151
+ end
152
+
153
+ def to_a
154
+ PROPERTY_NAME_TO_ATTRIBUTE_MAP.map do |property_name, attribute|
155
+ [property_name, public_send(attribute)]
156
+ end
157
+ end
158
+
159
+ def to_h
160
+ PROPERTY_NAME_TO_ATTRIBUTE_MAP.values.to_h {|attribute| [attribute, public_send(attribute)] }
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Meibo
4
+ class Organization
5
+ TYPES = {
6
+ department: 'department',
7
+ school: 'school',
8
+ district: 'district',
9
+ local: 'local',
10
+ state: 'state',
11
+ national: 'national'
12
+ }.freeze
13
+
14
+ DataModel.define(
15
+ self,
16
+ attribute_name_to_header_field_map: {
17
+ sourced_id: 'sourcedId',
18
+ status: 'status',
19
+ date_last_modified: 'dateLastModified',
20
+ name: 'name',
21
+ type: 'type',
22
+ identifier: 'identifier',
23
+ parent_sourced_id: 'parentSourcedId'
24
+ },
25
+ converters: {
26
+ datetime: [:date_last_modified],
27
+ enum: { type: [*TYPES.values, ENUM_EXT_PATTERN].freeze },
28
+ required: [:sourced_id, :name, :type],
29
+ status: [:status]
30
+ }
31
+ )
32
+
33
+ def initialize(sourced_id:, status: nil, date_last_modified: nil, name:, type:, identifier: nil, parent_sourced_id: (type == TYPES[:district] ? 'NULL' : nil), **extension_fields)
34
+ @sourced_id = sourced_id
35
+ @status = status
36
+ @date_last_modified = date_last_modified
37
+ @name = name
38
+ @type = type
39
+ @identifier = identifier
40
+ @parent_sourced_id = parent_sourced_id
41
+ @extension_fields = extension_fields
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Meibo
4
+ class OrganizationSet < DataSet
5
+ def check_semantically_consistent
6
+ super
7
+
8
+ each do |organization|
9
+ next unless organization.parent_sourced_id
10
+
11
+ find_by_sourced_id(organization.parent_sourced_id)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,138 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'csv'
4
+ require 'zip'
5
+
6
+ module Meibo
7
+ class Reader
8
+ def self.open(file_path, profile: BaseProfile)
9
+ Zip::File.open(file_path) do |zipfile|
10
+ yield new(zipfile: zipfile, profile: profile)
11
+ end
12
+ end
13
+
14
+ attr_reader :profile, :zipfile
15
+
16
+ def initialize(zipfile:, profile:)
17
+ @profile = profile
18
+ @zipfile = zipfile
19
+ end
20
+
21
+ def academic_sessions
22
+ each_academic_session.to_a
23
+ end
24
+
25
+ def classes
26
+ each_class.to_a
27
+ end
28
+
29
+ def courses
30
+ each_course.to_a
31
+ end
32
+
33
+ def demographics
34
+ each_demographic.to_a
35
+ end
36
+
37
+ def enrollments
38
+ each_enrollment.to_a
39
+ end
40
+
41
+ def organizations
42
+ each_organization.to_a
43
+ end
44
+
45
+ def roles
46
+ each_role.to_a
47
+ end
48
+
49
+ def users
50
+ each_user.to_a
51
+ end
52
+
53
+ def user_profiles
54
+ each_user_profile.to_a
55
+ end
56
+
57
+ def each_academic_session(&block)
58
+ read_data(:file_academic_sessions, &block)
59
+ end
60
+
61
+ def each_class(&block)
62
+ read_data(:file_classes, &block)
63
+ end
64
+
65
+ def each_course(&block)
66
+ read_data(:file_courses, &block)
67
+ end
68
+
69
+ def each_demographic(&block)
70
+ read_data(:file_demographics, &block)
71
+ end
72
+
73
+ def each_enrollment(&block)
74
+ read_data(:file_enrollments, &block)
75
+ end
76
+
77
+ def each_organization(&block)
78
+ read_data(:file_orgs, &block)
79
+ end
80
+
81
+ def each_role(&block)
82
+ read_data(:file_roles, &block)
83
+ end
84
+
85
+ def each_user_profile(&block)
86
+ read_data(:file_user_profiles, &block)
87
+ end
88
+
89
+ def each_user(&block)
90
+ read_data(:file_users, &block)
91
+ end
92
+
93
+ def manifest
94
+ @manifest ||= begin
95
+ filename = Meibo::Manifest.filename
96
+ raise CsvFileNotFoundError, "#{filename} not found" unless file_entry?(filename)
97
+
98
+ csv = @zipfile.read(filename)
99
+ Meibo::Manifest.parse(csv)
100
+ end
101
+ end
102
+
103
+ def load_bulk_files
104
+ bulk_file_attributes = manifest.file_attributes(processing_mode: Meibo::Manifest::PROCESSING_MODES[:bulk])
105
+ {
106
+ file_academic_sessions: :academic_sessions,
107
+ file_classes: :classes,
108
+ file_courses: :courses,
109
+ file_demographics: :demographics,
110
+ file_enrollments: :enrollments,
111
+ file_orgs: :organizations,
112
+ file_roles: :roles,
113
+ file_users: :users,
114
+ file_user_profiles: :user_profiles
115
+ }.filter_map {|file_attribute, data_method|
116
+ if bulk_file_attributes.include?(file_attribute)
117
+ [data_method, public_send(data_method).to_a]
118
+ end
119
+ }.to_h
120
+ end
121
+
122
+ def file_entry?(filename)
123
+ @zipfile.get_entry(filename).file?
124
+ rescue Errno::ENOENT
125
+ false
126
+ end
127
+
128
+ private
129
+
130
+ def read_data(file_attribute, &block)
131
+ filename = Manifest.filename_for(file_attribute)
132
+ raise CsvFileNotFoundError, "#{filename} not found" unless file_entry?(filename)
133
+
134
+ csv = @zipfile.read(filename)
135
+ profile.data_model_for(file_attribute).parse(csv, &block)
136
+ end
137
+ end
138
+ end
data/lib/meibo/role.rb ADDED
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Meibo
4
+ class Role
5
+ TYPES = {
6
+ primary: 'primary',
7
+ secondary: 'secondary'
8
+ }.freeze
9
+
10
+ ROLES = {
11
+ aide: 'aide',
12
+ counselor: 'counselor',
13
+ district_administrator: 'districtAdministrator',
14
+ guardian: 'guardian',
15
+ parent: 'parent',
16
+ principal: 'principal',
17
+ proctor: 'proctor',
18
+ relative: 'relative',
19
+ site_administrator: 'siteAdministrator',
20
+ student: 'student',
21
+ system_administrator: 'systemAdministrator',
22
+ teacher: 'teacher'
23
+ }.freeze
24
+
25
+ DataModel.define(
26
+ self,
27
+ attribute_name_to_header_field_map: {
28
+ sourced_id: 'sourcedId',
29
+ status: 'status',
30
+ date_last_modified: 'dateLastModified',
31
+ user_sourced_id: 'userSourcedId',
32
+ role_type: 'roleType',
33
+ role: 'role',
34
+ begin_date: 'beginDate',
35
+ end_date: 'endDate',
36
+ org_sourced_id: 'orgSourcedId',
37
+ user_profile_sourced_id: 'userProfileSourcedId'
38
+ }.freeze,
39
+ converters: {
40
+ date: [:begin_date, :end_date].freeze,
41
+ datetime: [:date_last_modified].freeze,
42
+ required: [:sourced_id, :user_sourced_id, :role_type, :role, :org_sourced_id].freeze,
43
+ enum: {
44
+ role: [*ROLES.values, ENUM_EXT_PATTERN].freeze,
45
+ role_type: TYPES.values.freeze
46
+ }.freeze,
47
+ status: [:status].freeze
48
+ }.freeze
49
+ )
50
+
51
+ def initialize(sourced_id:, status: nil, date_last_modified: nil, user_sourced_id:, role_type:, role:, begin_date: nil, end_date: nil, org_sourced_id:, user_profile_sourced_id: nil, **extension_fields)
52
+ @sourced_id = sourced_id
53
+ @status = status
54
+ @date_last_modified = date_last_modified
55
+ @user_sourced_id = user_sourced_id
56
+ @role_type = role_type
57
+ @role = role
58
+ @begin_date = begin_date
59
+ @end_date = end_date
60
+ @org_sourced_id = org_sourced_id
61
+ @user_profile_sourced_id = user_profile_sourced_id
62
+ @extension_fields = extension_fields
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Meibo
4
+ class RoleSet < DataSet
5
+ def initialize(data, organization_set:, user_set:, user_profile_set:)
6
+ super(data)
7
+ @organization_set = organization_set
8
+ @user_set = user_set
9
+ @user_profile_set = user_profile_set
10
+ end
11
+
12
+ def check_semantically_consistent
13
+ super
14
+
15
+ each do |role|
16
+ @organization_set.find_by_sourced_id(role.org_sourced_id)
17
+ @user_set.find_by_sourced_id(role.user_sourced_id)
18
+
19
+ if role.user_profile_sourced_id
20
+ @user_profile_set.find_by_sourced_id(role.user_profile_sourced_id)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end