Module: Senkyoshi
- Included in:
- OutcomeDefinition, RuleCriteria, WikiPage
- Defined in:
- lib/senkyoshi/version.rb,
lib/senkyoshi.rb,
lib/senkyoshi/tasks.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/link.rb,
lib/senkyoshi/models/quiz.rb,
lib/senkyoshi/models/rule.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/heirarchy.rb,
lib/senkyoshi/models/assessment.rb,
lib/senkyoshi/models/assignment.rb,
lib/senkyoshi/models/attachment.rb,
lib/senkyoshi/models/course_toc.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/rule_criteria.rb,
lib/senkyoshi/models/scorm_package.rb,
lib/senkyoshi/models/grade_criteria.rb,
lib/senkyoshi/models/questions/essay.rb,
lib/senkyoshi/models/assignment_group.rb,
lib/senkyoshi/models/module_converter.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/grade_range_criteria.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/grade_completed_criteria.rb,
lib/senkyoshi/models/questions/short_response.rb,
lib/senkyoshi/models/content_reviewed_criteria.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/grade_range_percent_criteria.rb,
lib/senkyoshi/models/questions/fill_in_blank_plus.rb
Overview
You should have received a copy of the GNU Affero General Public License along with this program. If not, see <www.gnu.org/licenses/>.
Defined Under Namespace
Classes: Announcement, Answer, Assessment, Assignment, AssignmentGroup, Attachment, Blog, Calculated, CanvasCourse, Collection, Configuration, Content, ContentFile, ContentReviewedCriteria, Course, CourseToc, EitherOr, Essay, ExternalUrl, FileResource, FileUpload, FillInBlank, FillInBlankPlus, Forum, GradeCompletedCriteria, GradeCriteria, GradeRangeCriteria, GradeRangePercentCriteria, Gradebook, Group, Heirarchy, HotSpot, JumbledSentence, Link, Matching, Module, ModuleConverter, ModuleItem, MultipleAnswer, MultipleChoice, NumericalQuestion, OpinionScale, Ordering, OutcomeDefinition, QTI, Question, QuestionBank, Quiz, QuizBowl, Resource, Rule, RuleCriteria, 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
- MAIN_CANVAS_MODULE =
"aj_main_module".freeze
- MASTER_MODULE =
"master_module".freeze
- VERSION =
"1.0.11".freeze
- RESOURCE_TYPE =
{
groups: "Group",
blog: "Blog",
announcement: "Announcement",
forum: "Forum",
course: "Course",
questestinterop: "QTI",
content: "Content",
staffinfo: "StaffInfo",
gradebook: "Gradebook",
rule: "Rule",
}.freeze
- PRE_RESOURCE_TYPE =
{
coursetoc: "CourseToc",
gradebook: "Gradebook",
link: "Link",
courseassessment: "QTI",
}.freeze
Class Attribute Summary collapse
Class Method Summary
collapse
-
.build_file(course, imscc_path, resources) ⇒ Object
-
.build_heirarchy(organizations, resources, course_toc) ⇒ 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, pre_data) ⇒ 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
-
.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(resources, zip_file, resource_xids, pre_data) ⇒ Object
-
.iterator_master(resources, zip_file) ⇒ Object
-
.parse(zip_path, imscc_path) ⇒ Object
-
.parse_and_process_single(zip_path, imscc_path) ⇒ Object
-
.pre_iterator(organizations, resources, zip_file) ⇒ Object
-
.read_file(zip_file, file_name) ⇒ Object
-
.reset ⇒ Object
-
.true?(obj) ⇒ Boolean
Class Attribute Details
.configuration ⇒ Object
44
45
46
|
# File 'lib/senkyoshi.rb', line 44
def self.configuration
@configuration ||= Configuration.new
end
|
Class Method Details
.build_file(course, imscc_path, resources) ⇒ Object
90
91
92
93
94
95
|
# File 'lib/senkyoshi.rb', line 90
def self.build_file(course, imscc_path, resources)
file = CanvasCc::CanvasCC::CartridgeCreator.new(course).create(Dir.tmpdir)
FileUtils.mv(file, imscc_path, force: true)
cleanup resources
puts "Created a file #{imscc_path}"
end
|
.build_heirarchy(organizations, resources, course_toc) ⇒ Object
170
171
172
173
174
175
176
|
# File 'lib/senkyoshi/xml_parser.rb', line 170
def self.build_heirarchy(organizations, resources, course_toc)
discussion_boards = resources.
search("resource[type=\"resource/x-bb-discussionboard\"]")
organizations.at("organization").children.flat_map do |item|
Heirarchy.item_iterator(item, course_toc, discussion_boards)
end
end
|
.cleanup(resources) ⇒ Object
Perform any necessary cleanup from creating canvas cartridge
100
101
102
|
# File 'lib/senkyoshi.rb', line 100
def self.cleanup(resources)
resources.each(&:cleanup)
end
|
52
53
54
|
# File 'lib/senkyoshi.rb', line 52
def self.configure
yield configuration
end
|
.connect_content(pre_data) ⇒ Object
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
|
# File 'lib/senkyoshi/xml_parser.rb', line 142
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
if pre_data["link"]
matching_link = pre_data["link"].detect do |link|
link[:referrer] == content[:file_name]
end
if matching_link
content[:referred_to_title] = matching_link[:referred_to_title]
end
end
end
pre_data["content"]
end
|
.create_canvas_course(resources, zip_name, pre_data) ⇒ Object
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
# File 'lib/senkyoshi.rb', line 104
def self.create_canvas_course(resources, zip_name, pre_data)
course = CanvasCc::CanvasCC::Models::Course.new
course.course_code = zip_name
resources.find_instances_not_of([Rule]).each do |resource|
course = resource.canvas_conversion(course, resources)
end
course = ModuleConverter.set_modules(course, pre_data)
resources.find_instances_of(Rule).each do |rule|
course = rule.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.
202
203
204
|
# File 'lib/senkyoshi/xml_parser.rb', line 202
def self.create_random_hex
"aj_" + SecureRandom.hex(32)
end
|
.get_attribute_value(xml_data, type) ⇒ Object
206
207
208
209
210
211
212
|
# File 'lib/senkyoshi/xml_parser.rb', line 206
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
222
223
224
225
226
227
228
|
# File 'lib/senkyoshi/xml_parser.rb', line 222
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
107
108
109
110
111
|
# File 'lib/senkyoshi/xml_parser.rb', line 107
def self.get_single_pre_data(pre_data, file)
pre_data.detect do |data_item|
data_item[:file_name] == file || data_item[:assignment_id] == file
end || { file_name: file }
end
|
.get_text(xml_data, type) ⇒ Object
214
215
216
217
218
219
220
|
# File 'lib/senkyoshi/xml_parser.rb', line 214
def self.get_text(xml_data, type)
value = ""
if xml_data.children.at(type)
value = xml_data.children.at(type).text
end
value
end
|
.initialize_course(canvas_file_path, blackboard_file_path) ⇒ Object
121
122
123
124
125
126
127
128
|
# File 'lib/senkyoshi.rb', line 121
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.
182
183
184
185
186
187
188
189
190
191
192
193
194
195
|
# File 'lib/senkyoshi/xml_parser.rb', line 182
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(resources, zip_file, resource_xids, pre_data) ⇒ Object
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
# File 'lib/senkyoshi/xml_parser.rb', line 91
def self.iterate_xml(resources, zip_file, resource_xids, pre_data)
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.flatten - ["", nil]
end
|
.iterator_master(resources, zip_file) ⇒ Object
113
114
115
116
117
118
119
120
121
122
123
124
125
|
# File 'lib/senkyoshi/xml_parser.rb', line 113
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 ? xml_data.name.downcase : "empty"
yield xml_data, type, file
end
end
end
|
.parse(zip_path, imscc_path) ⇒ Object
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
# File 'lib/senkyoshi.rb', line 56
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-") }
xml = Nokogiri::XML.parse(manifest)
xml_resources = xml.at("resources")
xml_organizations = xml.at("organizations")
pre_data = Senkyoshi.pre_iterator(xml_organizations, xml_resources, file)
resources.add(
Senkyoshi.iterate_xml(xml_resources, file, resource_xids, pre_data),
)
course = create_canvas_course(resources, zip_path, pre_data)
build_file(course, imscc_path, resources)
end
end
|
.parse_and_process_single(zip_path, imscc_path) ⇒ Object
80
81
82
|
# File 'lib/senkyoshi.rb', line 80
def self.parse_and_process_single(zip_path, imscc_path)
Senkyoshi.parse(zip_path, imscc_path)
end
|
.pre_iterator(organizations, resources, zip_file) ⇒ Object
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
# File 'lib/senkyoshi/xml_parser.rb', line 127
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["content"] = build_heirarchy(organizations, resources,
pre_data["coursetoc"]) - ["", nil]
pre_data = connect_content(pre_data)
end
|
.read_file(zip_file, file_name) ⇒ Object
84
85
86
87
88
|
# File 'lib/senkyoshi.rb', line 84
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
48
49
50
|
# File 'lib/senkyoshi.rb', line 48
def self.reset
@configuration = Configuration.new
end
|
.true?(obj) ⇒ Boolean
130
131
132
|
# File 'lib/senkyoshi.rb', line 130
def self.true?(obj)
obj.to_s == "true"
end
|