module Senkyoshi

You should have received a copy of the GNU Affero General Public License along with this program. If not, see <www.gnu.org/licenses/>.

You should have received a copy of the GNU Affero General Public License along with this program. If not, see <www.gnu.org/licenses/>.

You should have received a copy of the GNU Affero General Public License along with this program. If not, see <www.gnu.org/licenses/>.

You should have received a copy of the GNU Affero General Public License along with this program. If not, see <www.gnu.org/licenses/>.

You should have received a copy of the GNU Affero General Public License along with this program. If not, see <www.gnu.org/licenses/>.

You should have received a copy of the GNU Affero General Public License along with this program. If not, see <www.gnu.org/licenses/>.

Constants

DIR_BASE
FILE_BASE
MAIN_CANVAS_MODULE
MASTER_MODULE
PRE_RESOURCE_TYPE
RESOURCE_TYPE
VERSION

Attributes

configuration[W]

Public Class Methods

build_file(course, imscc_path, resources) click to toggle source
# 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) click to toggle source
# 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) click to toggle source

Perform any necessary cleanup from creating canvas cartridge

# File lib/senkyoshi.rb, line 100
def self.cleanup(resources)
  resources.each(&:cleanup)
end
configuration() click to toggle source
# File lib/senkyoshi.rb, line 44
def self.configuration
  @configuration ||= Configuration.new
end
configure() { |configuration| ... } click to toggle source
# File lib/senkyoshi.rb, line 52
def self.configure
  yield configuration
end
connect_content(pre_data) click to toggle source
# 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) click to toggle source
# 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

  # Wait until after we set modules to convert Rules
  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() click to toggle source

Create a random hex prepended with aj_ This is because the instructure qti migration tool requires the first character to be a letter.

# File lib/senkyoshi/xml_parser.rb, line 202
def self.create_random_hex
  "aj_" + SecureRandom.hex(32)
end
get_attribute_value(xml_data, type) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source

Iterate through course files and create new SenkyoshiFile for each non-metadata file.

# 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) click to toggle source
# 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) { |xml_data, type, file| ... } click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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() click to toggle source
# File lib/senkyoshi.rb, line 48
def self.reset
  @configuration = Configuration.new
end
true?(obj) click to toggle source
# File lib/senkyoshi.rb, line 130
def self.true?(obj)
  obj.to_s == "true"
end