Module: Senkyoshi
- Included in:
- OutcomeDefinition, WikiPage
- Defined in:
- lib/senkyoshi.rb,
lib/senkyoshi/tasks.rb,
lib/senkyoshi/version.rb,
lib/senkyoshi/collection.rb,
lib/senkyoshi/models/qti.rb,
lib/senkyoshi/xml_parser.rb,
lib/senkyoshi/models/blog.rb,
lib/senkyoshi/models/file.rb,
lib/senkyoshi/models/quiz.rb,
lib/senkyoshi/models/forum.rb,
lib/senkyoshi/models/group.rb,
lib/senkyoshi/canvas_course.rb,
lib/senkyoshi/configuration.rb,
lib/senkyoshi/models/answer.rb,
lib/senkyoshi/models/course.rb,
lib/senkyoshi/models/module.rb,
lib/senkyoshi/models/survey.rb,
lib/senkyoshi/models/content.rb,
lib/senkyoshi/models/question.rb,
lib/senkyoshi/models/resource.rb,
lib/senkyoshi/models/wikipage.rb,
lib/senkyoshi/models/gradebook.rb,
lib/senkyoshi/models/assessment.rb,
lib/senkyoshi/models/assignment.rb,
lib/senkyoshi/models/attachment.rb,
lib/senkyoshi/models/staff_info.rb,
lib/senkyoshi/models/module_item.rb,
lib/senkyoshi/models/announcement.rb,
lib/senkyoshi/models/content_file.rb,
lib/senkyoshi/models/external_url.rb,
lib/senkyoshi/models/file_resource.rb,
lib/senkyoshi/models/question_bank.rb,
lib/senkyoshi/models/scorm_package.rb,
lib/senkyoshi/models/questions/essay.rb,
lib/senkyoshi/models/assignment_group.rb,
lib/senkyoshi/models/outcome_definition.rb,
lib/senkyoshi/models/questions/hot_spot.rb,
lib/senkyoshi/models/questions/matching.rb,
lib/senkyoshi/models/questions/ordering.rb,
lib/senkyoshi/models/questions/either_or.rb,
lib/senkyoshi/models/questions/numerical.rb,
lib/senkyoshi/models/questions/quiz_bowl.rb,
lib/senkyoshi/models/questions/calculated.rb,
lib/senkyoshi/models/questions/true_false.rb,
lib/senkyoshi/models/questions/file_upload.rb,
lib/senkyoshi/models/questions/fill_in_blank.rb,
lib/senkyoshi/models/questions/opinion_scale.rb,
lib/senkyoshi/models/questions/short_response.rb,
lib/senkyoshi/models/questions/multiple_answer.rb,
lib/senkyoshi/models/questions/multiple_choice.rb,
lib/senkyoshi/models/questions/jumbled_sentence.rb,
lib/senkyoshi/models/questions/fill_in_blank_plus.rb
Defined Under Namespace
Classes: Announcement, Answer, Assessment, Assignment, AssignmentGroup, Attachment, Blog, Calculated, CanvasCourse, Collection, Configuration, Content, ContentFile, Course, EitherOr, Essay, ExternalUrl, FileResource, FileUpload, FillInBlank, FillInBlankPlus, Forum, Gradebook, Group, HotSpot, JumbledSentence, Matching, Module, ModuleItem, MultipleAnswer, MultipleChoice, NumericalQuestion, OpinionScale, Ordering, OutcomeDefinition, QTI, Question, QuestionBank, Quiz, QuizBowl, Resource, ScormPackage, SenkyoshiFile, ShortResponse, StaffInfo, Survey, Tasks, TrueFalse, WikiPage
Constant Summary
collapse
- FILE_BASE =
"$IMS-CC-FILEBASE$".freeze
- DIR_BASE =
"$CANVAS_COURSE_REFERENCE$/files/folder".freeze
- VERSION =
"1.0.3".freeze
- RESOURCE_TYPE =
{
groups: "Group",
blog: "Blog",
announcement: "Announcement",
forum: "Forum",
course: "Course",
questestinterop: "QTI",
content: "Content",
staffinfo: "StaffInfo",
gradebook: "Gradebook",
}.freeze
- PRE_RESOURCE_TYPE =
{
content: "Content",
gradebook: "Gradebook",
courseassessment: "QTI",
}.freeze
Class Attribute Summary collapse
Class Method Summary
collapse
-
.build_file(course, imscc_path, resources) ⇒ Object
-
.build_heirarchy(organizations, pre_data) ⇒ Object
-
.cleanup(resources) ⇒ Object
Perform any necessary cleanup from creating canvas cartridge.
-
.configure {|configuration| ... } ⇒ Object
-
.connect_content(pre_data) ⇒ Object
-
.create_canvas_course(resources, zip_name) ⇒ Object
-
.create_random_hex ⇒ Object
Create a random hex prepended with aj_ This is because the instructure qti migration tool requires the first character to be a letter.
-
.get_attribute_value(xml_data, type) ⇒ Object
-
.get_description(xml_data) ⇒ Object
-
.get_single_pre_data(pre_data, file) ⇒ Object
-
.get_text(xml_data, type) ⇒ Object
-
.get_title(organizations, content) ⇒ Object
-
.initialize_course(canvas_file_path, blackboard_file_path) ⇒ Object
-
.iterate_files(zipfile) ⇒ Object
Iterate through course files and create new SenkyoshiFile for each non-metadata file.
-
.iterate_xml(organizations, resources, zip_file, resource_xids) ⇒ Object
-
.iterator_master(resources, zip_file) ⇒ Object
-
.parse(zip_path, imscc_path) ⇒ Object
-
.parse_and_process_single(zip_path, imscc_path) ⇒ Object
-
.parse_manifest(zip_file, manifest, resource_xids) ⇒ Object
-
.pre_iterator(organizations, resources, zip_file) ⇒ Object
-
.read_file(zip_file, file_name) ⇒ Object
-
.reset ⇒ Object
Instance Method Summary
collapse
Class Attribute Details
.configuration ⇒ Object
23
24
25
|
# File 'lib/senkyoshi.rb', line 23
def self.configuration
@configuration ||= Configuration.new
end
|
Class Method Details
.build_file(course, imscc_path, resources) ⇒ Object
61
62
63
64
65
66
67
|
# File 'lib/senkyoshi.rb', line 61
def self.build_file(course, imscc_path, resources)
folder = File.dirname(imscc_path)
file = CanvasCc::CanvasCC::CartridgeCreator.new(course).create(folder)
File.rename(file, imscc_path)
cleanup resources
puts "Created a file #{imscc_path}"
end
|
.build_heirarchy(organizations, pre_data) ⇒ Object
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
|
# File 'lib/senkyoshi/xml_parser.rb', line 142
def self.build_heirarchy(organizations, pre_data)
unset_id = "{unset id}"
parents = pre_data.
select { |p| p[:parent_id] == unset_id }
parents_ids = parents.map { |u| u[:id] }
pre_data.each do |content|
parent_id = content[:parent_id]
parent = pre_data.detect { |p| p[:id] == parent_id }
if parent_id == unset_id
content[:title] = get_title(organizations, content)
elsif parent[:parent_id] == unset_id
content[:parent_title] = parent[:title]
end
next if parents_ids.include?(content[:id])
next if parents_ids.include?(parent_id)
parents_ids << parent_id
parent[:parent_id] = parent[:id]
parent[:parent_title] = nil
end
end
|
.cleanup(resources) ⇒ Object
Perform any necessary cleanup from creating canvas cartridge
72
73
74
|
# File 'lib/senkyoshi.rb', line 72
def self.cleanup(resources)
resources.each(&:cleanup)
end
|
31
32
33
|
# File 'lib/senkyoshi.rb', line 31
def self.configure
yield configuration
end
|
.connect_content(pre_data) ⇒ Object
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
# File 'lib/senkyoshi/xml_parser.rb', line 126
def self.connect_content(pre_data)
pre_data["content"].each do |content|
if pre_data["gradebook"]
gradebook = pre_data["gradebook"].first.
detect { |g| g[:content_id] == content[:file_name] }
content.merge!(gradebook) if gradebook
end
if pre_data["courseassessment"]
course_assessment = pre_data["courseassessment"].
detect { |ca| ca[:original_file_name] == content[:assignment_id] }
content.merge!(course_assessment) if course_assessment
end
end
pre_data["content"]
end
|
.create_canvas_course(resources, zip_name) ⇒ Object
76
77
78
79
80
81
82
83
|
# File 'lib/senkyoshi.rb', line 76
def self.create_canvas_course(resources, zip_name)
course = CanvasCc::CanvasCC::Models::Course.new
course.course_code = zip_name
resources.each do |resource|
course = resource.canvas_conversion(course, resources)
end
course
end
|
.create_random_hex ⇒ Object
Create a random hex prepended with aj_ This is because the instructure qti migration tool requires the first character to be a letter.
192
193
194
|
# File 'lib/senkyoshi/xml_parser.rb', line 192
def self.create_random_hex
"aj_" + SecureRandom.hex(32)
end
|
.get_attribute_value(xml_data, type) ⇒ Object
196
197
198
199
200
201
202
|
# File 'lib/senkyoshi/xml_parser.rb', line 196
def self.get_attribute_value(xml_data, type)
value = ""
if xml_data.children.at(type).attributes["value"]
value = xml_data.children.at(type).attributes["value"].value
end
value
end
|
.get_description(xml_data) ⇒ Object
212
213
214
215
216
217
218
|
# File 'lib/senkyoshi/xml_parser.rb', line 212
def self.get_description(xml_data)
value = ""
if xml_data.children.at("DESCRIPTION")
value = xml_data.children.at("DESCRIPTION").text
end
value
end
|
.get_single_pre_data(pre_data, file) ⇒ Object
93
94
95
96
97
|
# File 'lib/senkyoshi/xml_parser.rb', line 93
def self.get_single_pre_data(pre_data, file)
pre_data.detect do |d|
d[:file_name] == file || d[:assignment_id] == file
end || { file_name: file }
end
|
.get_text(xml_data, type) ⇒ Object
204
205
206
207
208
209
210
|
# File 'lib/senkyoshi/xml_parser.rb', line 204
def self.get_text(xml_data, type)
value = ""
if xml_data.children.at(type)
value = xml_data.children.at(type).text
end
value
end
|
.get_title(organizations, content) ⇒ Object
163
164
165
166
|
# File 'lib/senkyoshi/xml_parser.rb', line 163
def self.get_title(organizations, content)
item = organizations.at("item[@identifierref=#{content[:file_name]}]")
item.parent.at("title").text
end
|
.initialize_course(canvas_file_path, blackboard_file_path) ⇒ Object
85
86
87
88
89
90
91
92
|
# File 'lib/senkyoshi.rb', line 85
def self.initialize_course(canvas_file_path, blackboard_file_path)
metadata = Senkyoshi::CanvasCourse.metadata_from_file(canvas_file_path)
Zip::File.open(blackboard_file_path, "rb") do |bb_zip|
course = Senkyoshi::CanvasCourse.from_metadata(metadata, bb_zip)
course.upload_content(canvas_file_path)
cleanup course.scorm_packages
end
end
|
.iterate_files(zipfile) ⇒ Object
Iterate through course files and create new SenkyoshiFile for each non-metadata file.
172
173
174
175
176
177
178
179
180
181
182
183
184
185
|
# File 'lib/senkyoshi/xml_parser.rb', line 172
def self.iterate_files(zipfile)
files = zipfile.entries.select(&:file?)
dir_names = zipfile.entries.map { |entry| File.dirname(entry.name) }.uniq
file_names = files.map(&:name)
entry_names = dir_names + file_names
scorm_paths = ScormPackage.find_scorm_paths(zipfile)
files.select do |file|
SenkyoshiFile.valid_file?(entry_names, scorm_paths, file)
end.
map { |file| SenkyoshiFile.new(file) }
end
|
.iterate_xml(organizations, resources, zip_file, resource_xids) ⇒ Object
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
# File 'lib/senkyoshi/xml_parser.rb', line 76
def self.iterate_xml(organizations, resources, zip_file, resource_xids)
pre_data = pre_iterator(organizations, resources, zip_file)
staff_info = StaffInfo.new
iterator_master(resources, zip_file) do |xml_data, type, file|
if RESOURCE_TYPE[type.to_sym]
single_pre_data = get_single_pre_data(pre_data, file)
res_class = Senkyoshi.const_get RESOURCE_TYPE[type.to_sym]
case type
when "staffinfo"
staff_info.iterate_xml(xml_data, single_pre_data)
else
res_class.from(xml_data, single_pre_data, resource_xids)
end
end
end
end
|
.iterator_master(resources, zip_file) ⇒ Object
99
100
101
102
103
104
105
106
107
108
109
110
|
# File 'lib/senkyoshi/xml_parser.rb', line 99
def self.iterator_master(resources, zip_file)
resources.children.map do |resource|
file_name = resource.attributes["file"].value
file = File.basename(file_name, ".dat")
if zip_file.find_entry(file_name)
data_file = Senkyoshi.read_file(zip_file, file_name)
xml_data = Nokogiri::XML.parse(data_file).children.first
type = xml_data.name.downcase
yield xml_data, type, file
end
end
end
|
.parse(zip_path, imscc_path) ⇒ Object
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
# File 'lib/senkyoshi.rb', line 35
def self.parse(zip_path, imscc_path)
Zip::File.open(zip_path) do |file|
manifest = read_file(file, "imsmanifest.xml")
resources = Senkyoshi::Collection.new
resources.add(Senkyoshi.iterate_files(file))
resource_xids = resources.resources.
map(&:xid).
select { |r| r.include?("xid-") }
resources.add(Senkyoshi.parse_manifest(file, manifest, resource_xids))
course = create_canvas_course(resources, zip_path)
build_file(course, imscc_path, resources)
end
end
|
.parse_and_process_single(zip_path, imscc_path) ⇒ Object
51
52
53
|
# File 'lib/senkyoshi.rb', line 51
def self.parse_and_process_single(zip_path, imscc_path)
Senkyoshi.parse(zip_path, imscc_path)
end
|
.parse_manifest(zip_file, manifest, resource_xids) ⇒ Object
68
69
70
71
72
73
74
|
# File 'lib/senkyoshi/xml_parser.rb', line 68
def self.parse_manifest(zip_file, manifest, resource_xids)
doc = Nokogiri::XML.parse(manifest)
resources = doc.at("resources")
organizations = doc.at("organizations")
iterate_xml(organizations, resources, zip_file, resource_xids).
flatten - ["", nil]
end
|
.pre_iterator(organizations, resources, zip_file) ⇒ Object
112
113
114
115
116
117
118
119
120
121
122
123
124
|
# File 'lib/senkyoshi/xml_parser.rb', line 112
def self.pre_iterator(organizations, resources, zip_file)
pre_data = {}
iterator_master(resources, zip_file) do |xml_data, type, file|
if PRE_RESOURCE_TYPE[type.to_sym]
res_class = Senkyoshi.const_get PRE_RESOURCE_TYPE[type.to_sym]
pre_data[type] ||= []
data = res_class.get_pre_data(xml_data, file)
pre_data[type].push(data) if data
end
end
pre_data = connect_content(pre_data)
build_heirarchy(organizations, pre_data)
end
|
.read_file(zip_file, file_name) ⇒ Object
55
56
57
58
59
|
# File 'lib/senkyoshi.rb', line 55
def self.read_file(zip_file, file_name)
zip_file.find_entry(file_name).get_input_stream.read
rescue NoMethodError
raise Exceptions::MissingFileError
end
|
.reset ⇒ Object
27
28
29
|
# File 'lib/senkyoshi.rb', line 27
def self.reset
@configuration = Configuration.new
end
|
Instance Method Details
#true?(obj) ⇒ Boolean
94
95
96
|
# File 'lib/senkyoshi.rb', line 94
def true?(obj)
obj.to_s == "true"
end
|