meibo 0.27.1 → 0.28.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.
- checksums.yaml +4 -4
- data/.envrc +4 -0
- data/.rubocop.yml +14 -2
- data/.rubocop_todo.yml +0 -7
- data/Gemfile.lock +157 -56
- data/README.md +14 -3
- data/flake.lock +188 -0
- data/flake.nix +55 -0
- data/gemset.nix +658 -0
- data/lib/meibo/academic_session.rb +21 -21
- data/lib/meibo/classroom.rb +47 -34
- data/lib/meibo/classroom_set.rb +5 -1
- data/lib/meibo/converter.rb +16 -18
- data/lib/meibo/course.rb +34 -21
- data/lib/meibo/course_set.rb +5 -1
- data/lib/meibo/data_model.rb +46 -36
- data/lib/meibo/data_set.rb +8 -10
- data/lib/meibo/demographic.rb +36 -36
- data/lib/meibo/enrollment.rb +22 -22
- data/lib/meibo/enrollment_set.rb +9 -2
- data/lib/meibo/eportal/v3/classroom.rb +11 -0
- data/lib/meibo/eportal/v3/course.rb +11 -0
- data/lib/meibo/eportal/v3/enrollment.rb +11 -0
- data/lib/meibo/eportal/v3/organization.rb +11 -0
- data/lib/meibo/eportal/v3/user.rb +16 -0
- data/lib/meibo/eportal/v3/user_profile.rb +11 -0
- data/lib/meibo/eportal/v3.rb +30 -0
- data/lib/meibo/eportal/v4/user.rb +37 -0
- data/lib/meibo/eportal/v4.rb +16 -0
- data/lib/meibo/eportal.rb +6 -0
- data/lib/meibo/errors.rb +27 -5
- data/lib/meibo/factory_bot/academic_session.rb +1 -1
- data/lib/meibo/factory_bot/classroom.rb +1 -1
- data/lib/meibo/factory_bot/course.rb +1 -1
- data/lib/meibo/factory_bot/demographic.rb +1 -1
- data/lib/meibo/factory_bot/enrollment.rb +1 -1
- data/lib/meibo/factory_bot/manifest.rb +0 -21
- data/lib/meibo/factory_bot/organization.rb +1 -1
- data/lib/meibo/factory_bot/role.rb +2 -4
- data/lib/meibo/factory_bot/user.rb +5 -5
- data/lib/meibo/factory_bot/user_profile.rb +1 -1
- data/lib/meibo/japan_k12_schools_profile/v1/enrollment.rb +16 -0
- data/lib/meibo/japan_k12_schools_profile/v1.rb +20 -0
- data/lib/meibo/japan_k12_schools_profile.rb +6 -0
- data/lib/meibo/japan_profile/v1_1/academic_session.rb +29 -0
- data/lib/meibo/japan_profile/v1_1/classroom.rb +27 -0
- data/lib/meibo/japan_profile/v1_1/course.rb +16 -0
- data/lib/meibo/japan_profile/v1_1/demographic.rb +11 -0
- data/lib/meibo/japan_profile/v1_1/enrollment.rb +47 -0
- data/lib/meibo/japan_profile/v1_1/organization.rb +16 -0
- data/lib/meibo/japan_profile/v1_1/organization_set.rb +23 -0
- data/lib/meibo/japan_profile/v1_1/user.rb +39 -0
- data/lib/meibo/japan_profile/v1_1/user_set.rb +17 -0
- data/lib/meibo/japan_profile/v1_1.rb +35 -0
- data/lib/meibo/japan_profile/v1_1_1.rb +9 -0
- data/lib/meibo/japan_profile/v1_2/user.rb +42 -0
- data/lib/meibo/japan_profile/v1_2.rb +20 -0
- data/lib/meibo/japan_profile.rb +6 -0
- data/lib/meibo/manifest.rb +41 -46
- data/lib/meibo/oneroster/v1_2/user.rb +43 -0
- data/lib/meibo/oneroster/v1_2.rb +62 -0
- data/lib/meibo/oneroster/v1_2_1.rb +22 -0
- data/lib/meibo/oneroster.rb +6 -0
- data/lib/meibo/organization.rb +17 -17
- data/lib/meibo/processing_mode.rb +53 -0
- data/lib/meibo/profile.rb +8 -4
- data/lib/meibo/profiles.rb +12 -120
- data/lib/meibo/pse_interoperability_standard/v5.rb +9 -0
- data/lib/meibo/pse_interoperability_standard/v6/enrollment.rb +11 -0
- data/lib/meibo/pse_interoperability_standard/v6.rb +18 -0
- data/lib/meibo/pse_interoperability_standard.rb +6 -0
- data/lib/meibo/reader.rb +24 -36
- data/lib/meibo/role.rb +24 -24
- data/lib/meibo/roster.rb +46 -47
- data/lib/meibo/user.rb +35 -35
- data/lib/meibo/user_profile.rb +20 -20
- data/lib/meibo/user_set.rb +1 -1
- data/lib/meibo/version.rb +1 -1
- data/lib/meibo.rb +10 -2
- data/meibo.gemspec +1 -1
- metadata +45 -28
- data/lib/meibo/eportal_v3/classroom.rb +0 -15
- data/lib/meibo/eportal_v3/course.rb +0 -15
- data/lib/meibo/eportal_v3/enrollment.rb +0 -15
- data/lib/meibo/eportal_v3/organization.rb +0 -15
- data/lib/meibo/eportal_v3/user.rb +0 -16
- data/lib/meibo/eportal_v3/user_profile.rb +0 -15
- data/lib/meibo/japan_profile/academic_session.rb +0 -29
- data/lib/meibo/japan_profile/classroom.rb +0 -24
- data/lib/meibo/japan_profile/course.rb +0 -18
- data/lib/meibo/japan_profile/demographic.rb +0 -15
- data/lib/meibo/japan_profile/enrollment.rb +0 -40
- data/lib/meibo/japan_profile/organization.rb +0 -20
- data/lib/meibo/japan_profile/organization_set.rb +0 -17
- data/lib/meibo/japan_profile/role_jp_m0.rb +0 -26
- data/lib/meibo/japan_profile/user.rb +0 -45
- data/lib/meibo/japan_profile/user_m0.rb +0 -12
- data/lib/meibo/japan_profile/user_set.rb +0 -14
- data/lib/meibo/manifest/processing_mode.rb +0 -41
- data/lib/meibo/user_m0.rb +0 -47
data/lib/meibo/classroom.rb
CHANGED
|
@@ -2,49 +2,62 @@
|
|
|
2
2
|
|
|
3
3
|
module Meibo
|
|
4
4
|
class Classroom
|
|
5
|
+
include DataModel
|
|
6
|
+
|
|
5
7
|
TYPES = {
|
|
6
8
|
homeroom: "homeroom",
|
|
7
9
|
scheduled: "scheduled"
|
|
8
10
|
}.freeze
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
status: [:status].freeze
|
|
42
|
-
}
|
|
12
|
+
define_attributes(
|
|
13
|
+
sourced_id: "sourcedId",
|
|
14
|
+
status: "status",
|
|
15
|
+
date_last_modified: "dateLastModified",
|
|
16
|
+
title: "title",
|
|
17
|
+
grades: "grades",
|
|
18
|
+
course_sourced_id: "courseSourcedId",
|
|
19
|
+
class_code: "classCode",
|
|
20
|
+
class_type: "classType",
|
|
21
|
+
location: "location",
|
|
22
|
+
school_sourced_id: "schoolSourcedId",
|
|
23
|
+
term_sourced_ids: "termSourcedIds",
|
|
24
|
+
subjects: "subjects",
|
|
25
|
+
subject_codes: "subjectCodes",
|
|
26
|
+
periods: "periods"
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
define_converters(
|
|
30
|
+
datetime: [:date_last_modified],
|
|
31
|
+
enum: {
|
|
32
|
+
class_type: [*TYPES.values, ENUM_EXT_PATTERN]
|
|
33
|
+
},
|
|
34
|
+
list: %i[
|
|
35
|
+
grades
|
|
36
|
+
term_sourced_ids
|
|
37
|
+
subjects
|
|
38
|
+
subject_codes
|
|
39
|
+
periods
|
|
40
|
+
],
|
|
41
|
+
required: %i[sourced_id title class_type course_sourced_id term_sourced_ids school_sourced_id],
|
|
42
|
+
status: [:status]
|
|
43
43
|
)
|
|
44
44
|
|
|
45
|
+
def self.parse(csv)
|
|
46
|
+
return to_enum(:parse, csv) unless block_given?
|
|
47
|
+
|
|
48
|
+
_parse(csv).with_index(1) do |row, line|
|
|
49
|
+
yield new(**row.to_h)
|
|
50
|
+
rescue SubjectsAndSubjectCodesLengthNotMatch
|
|
51
|
+
index = attribute_names.index(:subjects)
|
|
52
|
+
field = row[index]
|
|
53
|
+
field_info = CSV::FieldInfo.new(index, line, :subjects, false)
|
|
54
|
+
raise InvalidDataTypeError.new(field: field, field_info: field_info)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
45
58
|
def initialize(sourced_id:, title:, course_sourced_id:, class_type:, school_sourced_id:, term_sourced_ids:, status: nil, date_last_modified: nil, grades: [],
|
|
46
59
|
class_code: nil, location: nil, subjects: [], subject_codes: [], periods: [], **extension_fields)
|
|
47
|
-
raise
|
|
60
|
+
raise SubjectsAndSubjectCodesLengthNotMatch unless subjects.is_a?(Array) && subject_codes.is_a?(Array) && subjects.size == subject_codes.size
|
|
48
61
|
|
|
49
62
|
@sourced_id = sourced_id
|
|
50
63
|
@status = status
|
data/lib/meibo/classroom_set.rb
CHANGED
|
@@ -7,7 +7,11 @@ module Meibo
|
|
|
7
7
|
|
|
8
8
|
each do |classroom|
|
|
9
9
|
school = roster.organizations.find(classroom.school_sourced_id)
|
|
10
|
-
|
|
10
|
+
unless school.school?
|
|
11
|
+
field = classroom.school_sourced_id
|
|
12
|
+
field_info = field_info_from(classroom, :school_sourced_id)
|
|
13
|
+
raise InvalidDataTypeError.new(field: field, field_info: field_info)
|
|
14
|
+
end
|
|
11
15
|
|
|
12
16
|
roster.courses.find(classroom.course_sourced_id)
|
|
13
17
|
|
data/lib/meibo/converter.rb
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "date"
|
|
4
|
-
require "set"
|
|
5
4
|
require "time"
|
|
6
5
|
|
|
7
6
|
module Meibo
|
|
@@ -22,7 +21,7 @@ module Meibo
|
|
|
22
21
|
year
|
|
23
22
|
].freeze
|
|
24
23
|
|
|
25
|
-
class << self
|
|
24
|
+
class << self # rubocop:disable Metrics/ClassLength
|
|
26
25
|
def build_header_field_to_attribute_converter(attribute_name_to_header_field_map)
|
|
27
26
|
header_field_to_attribute_name_map = attribute_name_to_header_field_map.to_h do |attribute, header_field|
|
|
28
27
|
[header_field, attribute]
|
|
@@ -82,7 +81,7 @@ module Meibo
|
|
|
82
81
|
when nil
|
|
83
82
|
nil
|
|
84
83
|
else
|
|
85
|
-
raise InvalidDataTypeError
|
|
84
|
+
raise InvalidDataTypeError.new(field: field, field_info: field_info)
|
|
86
85
|
end
|
|
87
86
|
else
|
|
88
87
|
field
|
|
@@ -108,7 +107,7 @@ module Meibo
|
|
|
108
107
|
begin
|
|
109
108
|
Date.strptime(field, "%Y-%m-%d")
|
|
110
109
|
rescue StandardError
|
|
111
|
-
raise InvalidDataTypeError
|
|
110
|
+
raise InvalidDataTypeError.new(field: field, field_info: field_info)
|
|
112
111
|
end
|
|
113
112
|
else
|
|
114
113
|
field
|
|
@@ -134,7 +133,7 @@ module Meibo
|
|
|
134
133
|
begin
|
|
135
134
|
Time.iso8601(field)
|
|
136
135
|
rescue StandardError
|
|
137
|
-
raise InvalidDataTypeError
|
|
136
|
+
raise InvalidDataTypeError.new(field: field, field_info: field_info)
|
|
138
137
|
end
|
|
139
138
|
else
|
|
140
139
|
field
|
|
@@ -148,7 +147,7 @@ module Meibo
|
|
|
148
147
|
return field unless field
|
|
149
148
|
|
|
150
149
|
enum = enum_definition[field_info.index]
|
|
151
|
-
raise InvalidDataTypeError if enum&.none? { |pat| pat.is_a?(String) ? field == pat : field.match?(pat) }
|
|
150
|
+
raise InvalidDataTypeError.new(field: field, field_info: field_info) if enum&.none? { |pat| pat.is_a?(String) ? field == pat : field.match?(pat) }
|
|
152
151
|
|
|
153
152
|
field
|
|
154
153
|
end
|
|
@@ -160,7 +159,7 @@ module Meibo
|
|
|
160
159
|
return field unless field
|
|
161
160
|
|
|
162
161
|
format = format_definition[field_info.index]
|
|
163
|
-
raise InvalidDataTypeError if format && !field.match?(format)
|
|
162
|
+
raise InvalidDataTypeError.new(field: field, field_info: field_info) if format && !field.match?(format)
|
|
164
163
|
|
|
165
164
|
field
|
|
166
165
|
end
|
|
@@ -169,7 +168,7 @@ module Meibo
|
|
|
169
168
|
def build_fullwidth_field_parser_converter(fullwidth_field_indexes)
|
|
170
169
|
fullwidth_field_indexes = fullwidth_field_indexes.dup.freeze
|
|
171
170
|
lambda do |field, field_info|
|
|
172
|
-
raise InvalidDataTypeError if field && fullwidth_field_indexes.include?(field_info.index) && field.match?(/[\p{In_Halfwidth_and_Fullwidth_Forms}&&\p{Katakana}]/)
|
|
171
|
+
raise InvalidDataTypeError.new(field: field, field_info: field_info) if field && fullwidth_field_indexes.include?(field_info.index) && field.match?(/[\p{In_Halfwidth_and_Fullwidth_Forms}&&\p{Katakana}]/)
|
|
173
172
|
|
|
174
173
|
field
|
|
175
174
|
end
|
|
@@ -180,7 +179,7 @@ module Meibo
|
|
|
180
179
|
lambda do |grades, field_info|
|
|
181
180
|
next grades unless grade_field_indexes.include?(field_info.index)
|
|
182
181
|
|
|
183
|
-
raise InvalidDataTypeError unless grades.all? { |grade| valid_grade.include?(grade) }
|
|
182
|
+
raise InvalidDataTypeError.new(field: grades, field_info: field_info) unless grades.all? { |grade| valid_grade.include?(grade) }
|
|
184
183
|
|
|
185
184
|
grades
|
|
186
185
|
end
|
|
@@ -193,7 +192,7 @@ module Meibo
|
|
|
193
192
|
begin
|
|
194
193
|
Integer(field, 10)
|
|
195
194
|
rescue StandardError
|
|
196
|
-
raise InvalidDataTypeError
|
|
195
|
+
raise InvalidDataTypeError.new(field: field, field_info: field_info)
|
|
197
196
|
end
|
|
198
197
|
else
|
|
199
198
|
field
|
|
@@ -236,11 +235,10 @@ module Meibo
|
|
|
236
235
|
def build_required_field_parser_converter(required_field_indexes)
|
|
237
236
|
required_field_indexes = required_field_indexes.dup.freeze
|
|
238
237
|
lambda do |field, field_info|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
field
|
|
238
|
+
return field unless required_field_indexes.include?(field_info.index)
|
|
239
|
+
return field if field && (!field.respond_to?(:empty?) || !field.empty?)
|
|
240
|
+
|
|
241
|
+
raise MissingDataError.new(field: field, field_info: field_info)
|
|
244
242
|
end
|
|
245
243
|
end
|
|
246
244
|
|
|
@@ -251,7 +249,7 @@ module Meibo
|
|
|
251
249
|
status_field_indexes = status_field_indexes.dup.freeze
|
|
252
250
|
lambda do |field, field_info|
|
|
253
251
|
if field && status_field_indexes.include?(field_info.index)
|
|
254
|
-
raise InvalidDataTypeError, "invalid status: #{field}" unless %w[active tobedeleted].include?(field)
|
|
252
|
+
raise InvalidDataTypeError.new(field: field, field_info: field_info), "invalid status: #{field}" unless %w[active tobedeleted].include?(field)
|
|
255
253
|
else
|
|
256
254
|
field
|
|
257
255
|
end
|
|
@@ -264,7 +262,7 @@ module Meibo
|
|
|
264
262
|
if user_ids_field_indexes.include?(field_info.index) && !field.all? do |user_id|
|
|
265
263
|
Meibo::User::USER_ID_FORMAT_REGEXP.match?(user_id)
|
|
266
264
|
end
|
|
267
|
-
raise InvalidDataTypeError
|
|
265
|
+
raise InvalidDataTypeError.new(field: field, field_info: field_info)
|
|
268
266
|
end
|
|
269
267
|
|
|
270
268
|
field
|
|
@@ -289,7 +287,7 @@ module Meibo
|
|
|
289
287
|
begin
|
|
290
288
|
Integer(field, 10)
|
|
291
289
|
rescue StandardError
|
|
292
|
-
raise InvalidDataTypeError
|
|
290
|
+
raise InvalidDataTypeError.new(field: field, field_info: field_info)
|
|
293
291
|
end
|
|
294
292
|
else
|
|
295
293
|
field
|
data/lib/meibo/course.rb
CHANGED
|
@@ -2,31 +2,44 @@
|
|
|
2
2
|
|
|
3
3
|
module Meibo
|
|
4
4
|
class Course
|
|
5
|
-
DataModel
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
5
|
+
include DataModel
|
|
6
|
+
|
|
7
|
+
define_attributes(
|
|
8
|
+
sourced_id: "sourcedId",
|
|
9
|
+
status: "status",
|
|
10
|
+
date_last_modified: "dateLastModified",
|
|
11
|
+
school_year_sourced_id: "schoolYearSourcedId",
|
|
12
|
+
title: "title",
|
|
13
|
+
course_code: "courseCode",
|
|
14
|
+
grades: "grades",
|
|
15
|
+
org_sourced_id: "orgSourcedId",
|
|
16
|
+
subjects: "subjects",
|
|
17
|
+
subject_codes: "subjectCodes"
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
define_converters(
|
|
21
|
+
datetime: [:date_last_modified],
|
|
22
|
+
list: %i[grades subjects subject_codes],
|
|
23
|
+
required: %i[sourced_id title org_sourced_id],
|
|
24
|
+
status: [:status]
|
|
25
25
|
)
|
|
26
26
|
|
|
27
|
+
def self.parse(csv)
|
|
28
|
+
return to_enum(:parse, csv) unless block_given?
|
|
29
|
+
|
|
30
|
+
_parse(csv).with_index(1) do |row, line|
|
|
31
|
+
yield new(**row.to_h)
|
|
32
|
+
rescue SubjectsAndSubjectCodesLengthNotMatch
|
|
33
|
+
index = attribute_names.index(:subjects)
|
|
34
|
+
field = row[index]
|
|
35
|
+
field_info = CSV::FieldInfo.new(index, line, :subjects, false)
|
|
36
|
+
raise InvalidDataTypeError.new(field: field, field_info: field_info)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
27
40
|
def initialize(sourced_id:, title:, org_sourced_id:, status: nil, date_last_modified: nil, school_year_sourced_id: nil,
|
|
28
41
|
course_code: nil, grades: [], subjects: [], subject_codes: [], **extension_fields)
|
|
29
|
-
raise
|
|
42
|
+
raise SubjectsAndSubjectCodesLengthNotMatch unless subjects.is_a?(Array) && subject_codes.is_a?(Array) && subjects.size == subject_codes.size
|
|
30
43
|
|
|
31
44
|
@sourced_id = sourced_id
|
|
32
45
|
@status = status
|
data/lib/meibo/course_set.rb
CHANGED
|
@@ -9,7 +9,11 @@ module Meibo
|
|
|
9
9
|
if course.school_year_sourced_id
|
|
10
10
|
school_year = roster.academic_sessions.find(course.school_year_sourced_id)
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
unless school_year.school_year?
|
|
13
|
+
field = school_year_sourced_id
|
|
14
|
+
field_info = field_info_from(course, :school_year_sourced_id)
|
|
15
|
+
raise InvalidDataTypeError.new(field: field, field_info: field_info)
|
|
16
|
+
end
|
|
13
17
|
end
|
|
14
18
|
|
|
15
19
|
roster.organizations.find(course.org_sourced_id)
|
data/lib/meibo/data_model.rb
CHANGED
|
@@ -5,13 +5,29 @@ require "csv"
|
|
|
5
5
|
module Meibo
|
|
6
6
|
module DataModel
|
|
7
7
|
module ClassMethods
|
|
8
|
+
def define_attributes(attribute_names_to_header_fields)
|
|
9
|
+
attribute_names_to_header_fields = attribute_names_to_header_fields.dup.freeze
|
|
10
|
+
attribute_names = attribute_names_to_header_fields.keys.freeze
|
|
11
|
+
header_fields = attribute_names_to_header_fields.values.freeze
|
|
12
|
+
define_class_attribute(:attribute_names_to_header_fields, attribute_names_to_header_fields)
|
|
13
|
+
define_class_attribute(:attribute_names, attribute_names)
|
|
14
|
+
define_class_attribute(:header_fields, header_fields)
|
|
15
|
+
|
|
16
|
+
attr_reader(*attribute_names, :extension_fields)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def define_converters(converters)
|
|
20
|
+
converters = converters.dup.freeze
|
|
21
|
+
define_class_attribute(:converters, converters)
|
|
22
|
+
define_header_converters
|
|
23
|
+
define_parser_converters(converters)
|
|
24
|
+
define_write_converters(converters)
|
|
25
|
+
end
|
|
26
|
+
|
|
8
27
|
def parse(csv)
|
|
9
28
|
return to_enum(:parse, csv) unless block_given?
|
|
10
29
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
CSV.parse(csv, encoding: Meibo::CSV_ENCODING, headers: true, converters: parser_converters,
|
|
14
|
-
header_converters: header_converters).each do |row|
|
|
30
|
+
_parse(csv) do |row|
|
|
15
31
|
yield new(**row.to_h)
|
|
16
32
|
end
|
|
17
33
|
end
|
|
@@ -24,44 +40,38 @@ module Meibo
|
|
|
24
40
|
end
|
|
25
41
|
raise ScrambledHeaderFieldsError unless actual_header_fields.take(header_fields.size) == header_fields
|
|
26
42
|
end
|
|
27
|
-
end
|
|
28
43
|
|
|
29
|
-
|
|
30
|
-
attribute_name_to_header_field_map = attribute_name_to_header_field_map.dup.freeze
|
|
31
|
-
attribute_names = attribute_name_to_header_field_map.keys.freeze
|
|
32
|
-
header_fields = attribute_name_to_header_field_map.values.freeze
|
|
33
|
-
converters = converters.dup.freeze
|
|
34
|
-
define_class_attribute(klass, :attribute_name_to_header_field_map, attribute_name_to_header_field_map)
|
|
35
|
-
define_class_attribute(klass, :attribute_names, attribute_names)
|
|
36
|
-
define_class_attribute(klass, :header_fields, header_fields)
|
|
37
|
-
define_class_attribute(klass, :converters, converters)
|
|
38
|
-
|
|
39
|
-
define_header_converters(klass, attribute_name_to_header_field_map)
|
|
40
|
-
define_parser_converters(klass, attribute_names: attribute_names, converters: converters)
|
|
41
|
-
define_write_converters(klass, attribute_names: attribute_names, converters: converters)
|
|
42
|
-
|
|
43
|
-
klass.attr_reader(*attribute_names, :extension_fields)
|
|
44
|
-
klass.extend(ClassMethods)
|
|
45
|
-
klass.include(self)
|
|
46
|
-
end
|
|
44
|
+
private
|
|
47
45
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
end
|
|
46
|
+
def _parse(csv, &)
|
|
47
|
+
validate_header_fields(CSV.parse_line(csv))
|
|
51
48
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
klass.define_singleton_method(:header_converters) { header_converters }
|
|
55
|
-
end
|
|
49
|
+
CSV.parse(csv, encoding: Meibo::CSV_ENCODING, headers: true, converters: parser_converters, header_converters: header_converters).each(&)
|
|
50
|
+
end
|
|
56
51
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
52
|
+
def define_class_attribute(attribute, value)
|
|
53
|
+
define_singleton_method(attribute) { value }
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def define_header_converters
|
|
57
|
+
header_converters = Converter.build_header_field_to_attribute_converter(attribute_names_to_header_fields)
|
|
58
|
+
define_class_attribute(:header_converters, header_converters)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def define_parser_converters(converters)
|
|
62
|
+
parser_converter = Converter.build_parser_converter(fields: attribute_names, converters: converters)
|
|
63
|
+
define_class_attribute(:parser_converters, parser_converter)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def define_write_converters(converters)
|
|
67
|
+
write_converter = Converter.build_write_converter(fields: attribute_names, converters: converters)
|
|
68
|
+
define_class_attribute(:write_converters, write_converter)
|
|
69
|
+
end
|
|
60
70
|
end
|
|
61
71
|
|
|
62
|
-
def self.
|
|
63
|
-
|
|
64
|
-
|
|
72
|
+
def self.included(base)
|
|
73
|
+
super
|
|
74
|
+
base.extend(ClassMethods)
|
|
65
75
|
end
|
|
66
76
|
|
|
67
77
|
def lineno
|
data/lib/meibo/data_set.rb
CHANGED
|
@@ -14,22 +14,14 @@ module Meibo
|
|
|
14
14
|
|
|
15
15
|
def <<(new_data)
|
|
16
16
|
raise DataNotFoundError, "sourcedIdがありません" unless new_data.sourced_id
|
|
17
|
-
|
|
18
|
-
if data_by_sourced_id.key?(new_data.sourced_id)
|
|
19
|
-
raise SourcedIdDuplicatedError,
|
|
20
|
-
"sourcedId\u304C\u91CD\u8907\u3057\u3066\u3044\u307E\u3059"
|
|
21
|
-
end
|
|
17
|
+
raise SourcedIdDuplicatedError, "sourcedIdが重複しています" if data_by_sourced_id.key?(new_data.sourced_id)
|
|
22
18
|
|
|
23
19
|
@data << new_data
|
|
24
20
|
@cache.clear
|
|
25
21
|
end
|
|
26
22
|
|
|
27
23
|
def check_semantically_consistent
|
|
28
|
-
unless @data.size == data_by_sourced_id.size
|
|
29
|
-
raise SourcedIdDuplicatedError,
|
|
30
|
-
"sourcedId\u304C\u91CD\u8907\u3057\u3066\u3044\u307E\u3059"
|
|
31
|
-
end
|
|
32
|
-
|
|
24
|
+
raise SourcedIdDuplicatedError, "sourcedIdが重複しています" unless @data.size == data_by_sourced_id.size
|
|
33
25
|
raise DataNotFoundError, "sourcedIdがありません" unless data_by_sourced_id[nil].nil?
|
|
34
26
|
end
|
|
35
27
|
|
|
@@ -81,5 +73,11 @@ module Meibo
|
|
|
81
73
|
def empty_set
|
|
82
74
|
@empty_set ||= new([])
|
|
83
75
|
end
|
|
76
|
+
|
|
77
|
+
def field_info_from(model, attribute_name)
|
|
78
|
+
index = model.class.attribute_names.index(attribute_name)
|
|
79
|
+
line = lineno(model)
|
|
80
|
+
CSV::FieldInfo.new(index, line, attribute_name, false)
|
|
81
|
+
end
|
|
84
82
|
end
|
|
85
83
|
end
|
data/lib/meibo/demographic.rb
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module Meibo
|
|
4
4
|
class Demographic
|
|
5
|
+
include DataModel
|
|
6
|
+
|
|
5
7
|
SEX = {
|
|
6
8
|
male: "male",
|
|
7
9
|
female: "female",
|
|
@@ -9,42 +11,40 @@ module Meibo
|
|
|
9
11
|
other: "other"
|
|
10
12
|
}.freeze
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
status: [:status].freeze
|
|
47
|
-
}
|
|
14
|
+
define_attributes(
|
|
15
|
+
sourced_id: "sourcedId",
|
|
16
|
+
status: "status",
|
|
17
|
+
date_last_modified: "dateLastModified",
|
|
18
|
+
birth_date: "birthDate",
|
|
19
|
+
sex: "sex",
|
|
20
|
+
american_indian_or_alaska_native: "americanIndianOrAlaskaNative",
|
|
21
|
+
asian: "asian",
|
|
22
|
+
black_or_african_american: "blackOrAfricanAmerican",
|
|
23
|
+
native_hawaiian_or_other_pacific_islander: "nativeHawaiianOrOtherPacificIslander",
|
|
24
|
+
white: "white",
|
|
25
|
+
demographic_race_two_or_more_races: "demographicRaceTwoOrMoreRaces",
|
|
26
|
+
hispanic_or_latino_ethnicity: "hispanicOrLatinoEthnicity",
|
|
27
|
+
country_of_birth_code: "countryOfBirthCode",
|
|
28
|
+
state_of_birth_abbreviation: "stateOfBirthAbbreviation",
|
|
29
|
+
city_of_birth: "cityOfBirth",
|
|
30
|
+
public_school_residence_status: "publicSchoolResidenceStatus"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
define_converters(
|
|
34
|
+
boolean: %i[
|
|
35
|
+
american_indian_or_alaska_native
|
|
36
|
+
asian
|
|
37
|
+
black_or_african_american
|
|
38
|
+
native_hawaiian_or_other_pacific_islander
|
|
39
|
+
white
|
|
40
|
+
demographic_race_two_or_more_races
|
|
41
|
+
hispanic_or_latino_ethnicity
|
|
42
|
+
],
|
|
43
|
+
date: [:birth_date],
|
|
44
|
+
datetime: [:date_last_modified],
|
|
45
|
+
enum: { sex: [*SEX.values, ENUM_EXT_PATTERN] },
|
|
46
|
+
required: [:sourced_id],
|
|
47
|
+
status: [:status]
|
|
48
48
|
)
|
|
49
49
|
|
|
50
50
|
def initialize(sourced_id:, status: nil, date_last_modified: nil, birth_date: nil, sex: nil,
|
data/lib/meibo/enrollment.rb
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module Meibo
|
|
4
4
|
class Enrollment
|
|
5
|
+
include DataModel
|
|
6
|
+
|
|
5
7
|
ROLES = {
|
|
6
8
|
administrator: "administrator",
|
|
7
9
|
proctor: "proctor",
|
|
@@ -9,28 +11,26 @@ module Meibo
|
|
|
9
11
|
teacher: "teacher"
|
|
10
12
|
}.freeze
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
status: [:status].freeze
|
|
33
|
-
}
|
|
14
|
+
define_attributes(
|
|
15
|
+
sourced_id: "sourcedId",
|
|
16
|
+
status: "status",
|
|
17
|
+
date_last_modified: "dateLastModified",
|
|
18
|
+
class_sourced_id: "classSourcedId",
|
|
19
|
+
school_sourced_id: "schoolSourcedId",
|
|
20
|
+
user_sourced_id: "userSourcedId",
|
|
21
|
+
role: "role",
|
|
22
|
+
primary: "primary",
|
|
23
|
+
begin_date: "beginDate",
|
|
24
|
+
end_date: "endDate"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
define_converters(
|
|
28
|
+
boolean: [:primary],
|
|
29
|
+
date: %i[begin_date end_date],
|
|
30
|
+
datetime: [:date_last_modified],
|
|
31
|
+
enum: { role: [*ROLES.values, ENUM_EXT_PATTERN] },
|
|
32
|
+
required: %i[sourced_id class_sourced_id school_sourced_id user_sourced_id role],
|
|
33
|
+
status: [:status]
|
|
34
34
|
)
|
|
35
35
|
|
|
36
36
|
def initialize(sourced_id:, class_sourced_id:, school_sourced_id:, user_sourced_id:, role:, status: nil,
|
data/lib/meibo/enrollment_set.rb
CHANGED
|
@@ -8,10 +8,17 @@ module Meibo
|
|
|
8
8
|
each do |enrollment|
|
|
9
9
|
roster.classes.find(enrollment.class_sourced_id)
|
|
10
10
|
school = roster.organizations.find(enrollment.school_sourced_id)
|
|
11
|
-
|
|
11
|
+
|
|
12
|
+
unless school.school?
|
|
13
|
+
field = enrollment.school_sourced_id
|
|
14
|
+
field_info = field_info_from(enrollment, :school_sourced_id)
|
|
15
|
+
raise InvalidDataTypeError.new(field: field, field_info: field_info)
|
|
16
|
+
end
|
|
12
17
|
|
|
13
18
|
roster.users.find(enrollment.user_sourced_id)
|
|
14
|
-
|
|
19
|
+
next if !enrollment.primary || enrollment.teacher?
|
|
20
|
+
|
|
21
|
+
raise InvalidDataTypeError.new(field: enrollment.primary, field_info: field_info_from(enrollment, :primary))
|
|
15
22
|
end
|
|
16
23
|
end
|
|
17
24
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Meibo
|
|
4
|
+
module Eportal
|
|
5
|
+
module V3
|
|
6
|
+
class Classroom < ::Meibo::JapanProfile::V1_1::Classroom
|
|
7
|
+
define_converters(superclass.converters.merge(fullwidth: [*superclass.converters[:fullwidth], :title, :location]))
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Meibo
|
|
4
|
+
module Eportal
|
|
5
|
+
module V3
|
|
6
|
+
class Course < ::Meibo::JapanProfile::V1_1::Course
|
|
7
|
+
define_converters(superclass.converters.merge(fullwidth: [*superclass.converters[:fullwidth], :title]))
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|