senkyoshi 1.0.3 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +13 -13
  3. data/lib/senkyoshi/collection.rb +16 -2
  4. data/lib/senkyoshi/configuration.rb +1 -1
  5. data/lib/senkyoshi/models/attachment.rb +2 -0
  6. data/lib/senkyoshi/models/content.rb +13 -25
  7. data/lib/senkyoshi/models/content_reviewed_criteria.rb +27 -0
  8. data/lib/senkyoshi/models/course.rb +4 -0
  9. data/lib/senkyoshi/models/course_toc.rb +21 -0
  10. data/lib/senkyoshi/models/file_resource.rb +14 -0
  11. data/lib/senkyoshi/models/forum.rb +14 -1
  12. data/lib/senkyoshi/models/grade_completed_criteria.rb +8 -0
  13. data/lib/senkyoshi/models/grade_criteria.rb +46 -0
  14. data/lib/senkyoshi/models/grade_range_criteria.rb +34 -0
  15. data/lib/senkyoshi/models/grade_range_percent_criteria.rb +28 -0
  16. data/lib/senkyoshi/models/gradebook.rb +6 -0
  17. data/lib/senkyoshi/models/heirarchy.rb +84 -0
  18. data/lib/senkyoshi/models/link.rb +15 -0
  19. data/lib/senkyoshi/models/module.rb +6 -0
  20. data/lib/senkyoshi/models/module_converter.rb +86 -0
  21. data/lib/senkyoshi/models/module_item.rb +8 -2
  22. data/lib/senkyoshi/models/outcome_definition.rb +10 -5
  23. data/lib/senkyoshi/models/qti.rb +1 -0
  24. data/lib/senkyoshi/models/question.rb +3 -2
  25. data/lib/senkyoshi/models/questions/matching.rb +5 -1
  26. data/lib/senkyoshi/models/questions/ordering.rb +5 -3
  27. data/lib/senkyoshi/models/rule.rb +46 -0
  28. data/lib/senkyoshi/models/rule_criteria.rb +146 -0
  29. data/lib/senkyoshi/models/wikipage.rb +12 -2
  30. data/lib/senkyoshi/version.rb +1 -1
  31. data/lib/senkyoshi/xml_parser.rb +35 -41
  32. data/lib/senkyoshi.rb +25 -5
  33. metadata +14 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d7187188584379d9e573651fe9147e7bb50497ca
4
- data.tar.gz: e595d29609ad508dfc443cf1543b0eecdd382ea5
3
+ metadata.gz: 26c7126fd95fa6cf136131c92014471f65e2089e
4
+ data.tar.gz: 6a6a5352177a859ddbb3ac1c83fe9ac465e927a9
5
5
  SHA512:
6
- metadata.gz: c16a2de94714d3b58e2acaa22e17eb58fae79c1e17a13d8c987660ae60ffc3bf8e17d733b7ae1bc116e9d8e672a37d910c0efcce2952caad8d00f9032f94766f
7
- data.tar.gz: 4d333e8b3a2e0b6000a41d3b49d28a2a2fe17f3add5a1b0248ecda417fa333df4a0aaae9e0c4d980d9bbf599c971ac449dd41adcb840b4726cdbbcc97c715343
6
+ metadata.gz: bb156e023de570e457222738c42a34af1433c279ad9879501fcd7ed82d5a76538f2e3bfc191832bdcdcd03a54f4486a87abe2fbc9783d2eb9d54a7b415dff903
7
+ data.tar.gz: fb795ce77619b24d9b0ec6e55eeba40ece4e82b5f325b8b48a39be08d970349e96828395a1c29e34d63e4b8d60cd96d16b8fe11c57b844a9b140b11f780bb400
data/README.md CHANGED
@@ -4,6 +4,10 @@ Senkyoshi converts exported Blackboard packages into Canvas .imscc packages. It
4
4
 
5
5
  ## Installation
6
6
 
7
+ #### Canvas
8
+ See [Senkyoshi Canvas Plugin](https://github.com/atomicjolt/senkyoshi_canvas_plugin) if you want to enable this in your canvas app.
9
+
10
+ #### CLI
7
11
  Add this line to your application's Gemfile:
8
12
 
9
13
  ```ruby
@@ -32,24 +36,24 @@ Create a `senkyoshi.yml` and add credentials:
32
36
  # Generally looks like https://< mycanvas_instance >/api
33
37
  :canvas_url: <canvas instance api url>
34
38
 
35
- # Canvas tokens can be generated at <my_canvas_url>/profile/setting provided
36
- # that the user has the required priviledges
39
+ # Canvas tokens can be generated at <my_canvas_url>/profile/settings provided
40
+ # that the user has the required privileges
37
41
  :canvas_token: <canvas token>
38
42
 
39
- # Url of scorm manager. This could the the adhesion app
43
+ # URL of SCORM manager. This could be the Adhesion app
40
44
  # [https://github.com/atomicjolt/adhesion]
41
45
  :scorm_url: <scorm manager url>
42
46
 
43
- # This should be the endpoint to launch a given scorm course, in the case of
44
- # adhesion this will look like https://<adhesion url>/scorm_course
47
+ # This should be the endpoint to launch a given SCORM course. In the case of
48
+ # Adhesion, this will look like https://<adhesion url>/scorm_course
45
49
  :scorm_launch_url: <scorm launch url>
46
50
 
47
- # This is the secret to authenticate requests to the scorm manager. In the case
48
- # of adhesion you can generate a shared secret by logging into the server and
51
+ # This is the secret to authenticate requests to the SCORM manager. In the case
52
+ # of Adhesion, you can generate a shared secret by logging into the server and
49
53
  # running rake shared_auth, which will generate and save a token
50
54
  :scorm_shared_auth: <scorm manager token>
51
55
 
52
- # The account or sub-account id. This can be :self, :default, or an id
56
+ # The account or sub-account ID. This can be :self, :default, or an ID
53
57
  :account_id: <id>
54
58
  ```
55
59
 
@@ -63,7 +67,7 @@ This will take all your files in your source folder and convert them to your out
63
67
 
64
68
  Run converting files in parallel:
65
69
  ```sh
66
- time rake senkyoshi:imscc -m
70
+ time rake senkyoshi:imscc -m # The `time` command is optional.
67
71
  ```
68
72
 
69
73
  Delete entire outputs folder:
@@ -90,7 +94,3 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/atomic
90
94
  ## License
91
95
 
92
96
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
93
-
94
-
95
- ### Things Not Quite Implemented
96
- People, Groups, Sets, Blogs
@@ -2,8 +2,8 @@ module Senkyoshi
2
2
  class Collection
3
3
  attr_reader :resources
4
4
 
5
- def initialize
6
- @resources = []
5
+ def initialize(resources = [])
6
+ @resources = resources
7
7
  end
8
8
 
9
9
  def add(resources)
@@ -16,6 +16,20 @@ module Senkyoshi
16
16
  end
17
17
  end
18
18
 
19
+ def find_by_id(id)
20
+ @resources.detect { |item| item.respond_to?(:id) && item.id == id }
21
+ end
22
+
23
+ def find_instances_of(class_name)
24
+ @resources.select { |res| res.class == class_name }
25
+ end
26
+
27
+ def find_instances_not_of(types)
28
+ @resources.select do |res|
29
+ types.each { |type| res.class != type }
30
+ end
31
+ end
32
+
19
33
  def each
20
34
  @resources.each do |resource|
21
35
  yield resource
@@ -20,7 +20,7 @@ module Senkyoshi
20
20
 
21
21
  def self._config
22
22
  @config ||= if File.exists? "senkyoshi.yml"
23
- YAML::load(File.read("senkyoshi.yml"))
23
+ YAML::safe_load(File.read("senkyoshi.yml"), [Symbol])
24
24
  else
25
25
  {}
26
26
  end
@@ -10,6 +10,8 @@ module Senkyoshi
10
10
  @module_type,
11
11
  @files.first.name,
12
12
  @url,
13
+ @indent,
14
+ @file_name,
13
15
  ).canvas_conversion
14
16
  self
15
17
  end
@@ -20,6 +20,7 @@ module Senkyoshi
20
20
  "x-bb-module-page" => "WikiPage",
21
21
  "x-bb-lesson-plan" => "WikiPage",
22
22
  "x-bb-syllabus" => "WikiPage",
23
+ "x-bb-courselink" => "WikiPage",
23
24
  }.freeze
24
25
 
25
26
  MODULE_TYPES = {
@@ -55,6 +56,8 @@ module Senkyoshi
55
56
  def iterate_xml(xml, pre_data)
56
57
  @points = pre_data[:points] || 0
57
58
  @parent_title = pre_data[:parent_title]
59
+ @indent = pre_data[:indent]
60
+ @file_name = pre_data[:file_name]
58
61
  @title = xml.xpath("/CONTENT/TITLE/@value").first.text
59
62
  @url = xml.at("URL")["value"]
60
63
  @body = xml.xpath("/CONTENT/BODY/TEXT").first.text
@@ -65,6 +68,7 @@ module Senkyoshi
65
68
  @type = xml.xpath("/CONTENT/RENDERTYPE/@value").first.text
66
69
  @parent_id = pre_data[:parent_id]
67
70
  @module_type = MODULE_TYPES[self.class.name]
71
+ @referred_to_title = pre_data[:referred_to_title]
68
72
 
69
73
  if pre_data[:assignment_id] && !pre_data[:assignment_id].empty?
70
74
  @id = pre_data[:assignment_id]
@@ -78,18 +82,14 @@ module Senkyoshi
78
82
  end
79
83
 
80
84
  def set_module
81
- module_item = ModuleItem.new(@title, @module_type, @id, @url)
82
- module_item.canvas_conversion
83
- end
84
-
85
- def self.get_pre_data(xml, file_name)
86
- id = xml.xpath("/CONTENT/@id").first.text
87
- parent_id = xml.xpath("/CONTENT/PARENTID/@value").first.text
88
- {
89
- id: id,
90
- parent_id: parent_id,
91
- file_name: file_name,
92
- }
85
+ ModuleItem.new(
86
+ @title,
87
+ @module_type,
88
+ @id,
89
+ @url,
90
+ @indent,
91
+ @file_name,
92
+ ).canvas_conversion
93
93
  end
94
94
 
95
95
  def canvas_conversion(course, _resources = nil)
@@ -97,19 +97,7 @@ module Senkyoshi
97
97
  end
98
98
 
99
99
  def create_module(course)
100
- course.canvas_modules ||= []
101
- cc_module = course.canvas_modules.
102
- detect { |a| a.identifier == @parent_id }
103
- if cc_module
104
- cc_module.module_items << @module_item
105
- else
106
- title = @parent_title || @title
107
- cc_module = Module.new(title, @parent_id)
108
- cc_module = cc_module.canvas_conversion
109
- cc_module.module_items << @module_item
110
- course.canvas_modules << cc_module
111
- end
112
- course
100
+ super(course)
113
101
  end
114
102
  end
115
103
  end
@@ -0,0 +1,27 @@
1
+ require "senkyoshi/models/rule_criteria"
2
+
3
+ module Senkyoshi
4
+ class ContentReviewedCriteria < RuleCriteria
5
+ attr_reader(:reviewed_content_id)
6
+
7
+ def initialize(id, negated, reviewed_content_id)
8
+ super(id, negated)
9
+ @reviewed_content_id = reviewed_content_id
10
+ end
11
+
12
+ def self.from_xml(xml)
13
+ id = RuleCriteria.get_id xml
14
+ negated = Senkyoshi.true? RuleCriteria.get_negated(xml)
15
+ reviewed_content_id = xml.xpath("./REVIEWED_CONTENT_ID/@value").text
16
+ ContentReviewedCriteria.new(id, negated, reviewed_content_id)
17
+ end
18
+
19
+ def get_foreign_id
20
+ @reviewed_content_id
21
+ end
22
+
23
+ def get_completion_type
24
+ COMPLETION_TYPES[:must_view]
25
+ end
26
+ end
27
+ end
@@ -34,5 +34,9 @@ module Senkyoshi
34
34
  course.conclude_at = @conclude_at
35
35
  course
36
36
  end
37
+
38
+ def self.master_module(course)
39
+ course.canvas_modules.detect { |a| a.title == MASTER_MODULE }
40
+ end
37
41
  end
38
42
  end
@@ -0,0 +1,21 @@
1
+ require "senkyoshi/models/resource"
2
+ require "senkyoshi/models/content_file"
3
+
4
+ module Senkyoshi
5
+ class CourseToc
6
+ def self.get_pre_data(xml, file_name)
7
+ target_type = xml.xpath("/COURSETOC/TARGETTYPE/@value").first.text
8
+ if target_type != "MODULE" || target_type != "DIVIDER"
9
+ title = xml.xpath("/COURSETOC/LABEL/@value").first.text
10
+ internal_handle = xml.xpath("/COURSETOC/INTERNALHANDLE/@value").
11
+ first.text
12
+ {
13
+ title: title,
14
+ target_type: target_type,
15
+ original_file: file_name,
16
+ internal_handle: internal_handle,
17
+ }
18
+ end
19
+ end
20
+ end
21
+ end
@@ -19,5 +19,19 @@ module Senkyoshi
19
19
  def iterate_xml(_xml, _pre_data)
20
20
  self
21
21
  end
22
+
23
+ def create_module(course)
24
+ course.canvas_modules ||= []
25
+ cc_module = Course.master_module(course)
26
+ if cc_module
27
+ cc_module.module_items << @module_item
28
+ else
29
+ cc_module = Module.new(MASTER_MODULE, MASTER_MODULE)
30
+ cc_module = cc_module.canvas_conversion
31
+ cc_module.module_items << @module_item
32
+ course.canvas_modules << cc_module
33
+ end
34
+ course
35
+ end
22
36
  end
23
37
  end
@@ -9,9 +9,19 @@ module Senkyoshi
9
9
  @discussion_type = "threaded"
10
10
  end
11
11
 
12
- def iterate_xml(data, _)
12
+ def iterate_xml(data, pre_data)
13
13
  @title = Senkyoshi.get_attribute_value(data, "TITLE")
14
14
  @text = Senkyoshi.get_text(data, "TEXT")
15
+ if pre_data[:internal_handle]
16
+ @module_item = ModuleItem.new(
17
+ @title,
18
+ "DiscussionTopic",
19
+ @id,
20
+ nil,
21
+ pre_data[:indent],
22
+ @id,
23
+ ).canvas_conversion
24
+ end
15
25
  self
16
26
  end
17
27
 
@@ -22,6 +32,9 @@ module Senkyoshi
22
32
  discussion.identifier = @id
23
33
  discussion.discussion_type = @discussion_type
24
34
  course.discussions << discussion
35
+ if @module_item
36
+ course = create_module(course)
37
+ end
25
38
  course
26
39
  end
27
40
  end
@@ -0,0 +1,8 @@
1
+ require "senkyoshi/models/grade_criteria"
2
+ module Senkyoshi
3
+ class GradeCompletedCriteria < GradeCriteria
4
+ def get_completion_type
5
+ COMPLETION_TYPES[:must_submit]
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,46 @@
1
+ require "senkyoshi/models/rule_criteria"
2
+ module Senkyoshi
3
+ class GradeCriteria < RuleCriteria
4
+ attr_reader(:outcome_def_id)
5
+
6
+ def initialize(id, outcome_def_id, negated)
7
+ super(id, negated)
8
+ @outcome_def_id = outcome_def_id
9
+ @foreign_content_id = nil
10
+ end
11
+
12
+ def get_foreign_id
13
+ @foreign_asidata_id || @foreign_content_id
14
+ end
15
+
16
+ def self.get_outcome_def_id(xml)
17
+ xml.xpath("./OUTCOME_DEFINITION_ID/@value").text
18
+ end
19
+
20
+ def self.from_xml(xml)
21
+ id = RuleCriteria.get_id xml
22
+ negated = Senkyoshi.true? RuleCriteria.get_negated xml
23
+ outcome_def_id = GradeCriteria.get_outcome_def_id xml
24
+ new(id, outcome_def_id, negated)
25
+ end
26
+
27
+ ##
28
+ # use gradebook to match outcome_definition_id to the
29
+ # assignment content id
30
+ ##
31
+ def set_foreign_ids(resources, outcome_def_id)
32
+ gradebook = resources.find_instances_of(Gradebook).first
33
+
34
+ outcome = gradebook.find_outcome_def(outcome_def_id)
35
+ if outcome
36
+ @foreign_content_id = outcome.content_id
37
+ @foreign_asidata_id = outcome.asidataid
38
+ end
39
+ end
40
+
41
+ def canvas_conversion(course, content_id, resources)
42
+ set_foreign_ids(resources, @outcome_def_id)
43
+ super(course, content_id, resources)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,34 @@
1
+ require "senkyoshi/models/grade_criteria"
2
+
3
+ module Senkyoshi
4
+ class GradeRangeCriteria < GradeCriteria
5
+ attr_reader(:min_score)
6
+
7
+ def initialize(id, outcome_def_id, negated, min_score)
8
+ super(id, outcome_def_id, negated)
9
+ @min_score = min_score
10
+ end
11
+
12
+ def self.get_min_score(xml)
13
+ xml.xpath("./MIN_SCORE/@value").text
14
+ end
15
+
16
+ def self.from_xml(xml)
17
+ id = RuleCriteria.get_id xml
18
+ negated = Senkyoshi.true? RuleCriteria.get_negated xml
19
+ outcome_def_id = GradeCriteria.get_outcome_def_id xml
20
+ min_score = GradeRangePercentCriteria.get_min_score xml
21
+ new(id, outcome_def_id, negated, min_score)
22
+ end
23
+
24
+ def get_completion_type
25
+ COMPLETION_TYPES[:min_score]
26
+ end
27
+
28
+ def make_completion(mod)
29
+ super(mod).tap do |completion_requirement|
30
+ completion_requirement.min_score = @min_score
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,28 @@
1
+ require "senkyoshi/models/grade_range_criteria"
2
+
3
+ module Senkyoshi
4
+ class GradeRangePercentCriteria < GradeRangeCriteria
5
+ def make_completion(mod)
6
+ super(mod).tap do |completion_requirement|
7
+ completion_requirement.min_score = GradeRangePercentCriteria.
8
+ get_percentage(@min_score, @points_possible)
9
+ end
10
+ end
11
+
12
+ def get_points_possible(resources, id)
13
+ resource = resources.find_by_id(id)
14
+ resource.points_possible if resource
15
+ end
16
+
17
+ def self.get_percentage(min_score, points_possible)
18
+ (min_score.to_f / 100) * points_possible.to_f
19
+ end
20
+
21
+ def canvas_conversion(course, content_id, resources)
22
+ set_foreign_ids(resources, @outcome_def_id)
23
+ @points_possible = get_points_possible(resources, get_foreign_id)
24
+
25
+ super(course, content_id, resources)
26
+ end
27
+ end
28
+ end
@@ -57,6 +57,12 @@ module Senkyoshi
57
57
  end
58
58
  end
59
59
 
60
+ def find_outcome_def(outcome_def_id)
61
+ @outcome_definitions.detect do |outcome_def|
62
+ outcome_def.id == outcome_def_id
63
+ end
64
+ end
65
+
60
66
  def canvas_conversion(course, resources = nil)
61
67
  convert_categories(course)
62
68
  @outcome_definitions.
@@ -0,0 +1,84 @@
1
+ require "senkyoshi/models/resource"
2
+
3
+ module Senkyoshi
4
+ class Heirarchy
5
+ def self.item_iterator(item, course_toc, discussion_boards)
6
+ if item.search("item").count.zero?
7
+ toc_item = setup_item(item, item.parent, course_toc)
8
+ toc_item[:indent] = 0
9
+ set_discussion_boards(discussion_boards, toc_item)
10
+ else
11
+ item.search("item").flat_map do |internal_item|
12
+ toc_item = setup_item(internal_item, item, course_toc)
13
+ toc_item[:indent] = get_indent(internal_item)
14
+ toc_item = set_discussion_boards(discussion_boards, toc_item)
15
+ toc_item
16
+ end
17
+ end
18
+ end
19
+
20
+ def self.get_indent(item, indent = -2)
21
+ return indent if item.parent.name == "organization"
22
+ indent += 1
23
+ get_indent(item.parent, indent)
24
+ end
25
+
26
+ def self.set_discussion_boards(discussion_boards, toc_item)
27
+ if toc_item[:internal_handle] == "discussion_board_entry"
28
+ resource = discussion_boards.select do |db|
29
+ title_attribute = db.attributes["title"] || db.attributes["bb:title"]
30
+ title_attribute.value == toc_item[:title]
31
+ end
32
+ if resource.count == 1
33
+ file_attribute = resource.first.attributes["file"] ||
34
+ resource.first.attributes["bb:file"]
35
+ toc_item[:file_name] = file_attribute.value.gsub(".dat", "")
36
+ end
37
+ end
38
+ toc_item
39
+ end
40
+
41
+ def self.setup_item(item, parent_item, course_toc)
42
+ if item.attributes["identifierref"]
43
+ title = item.at("title").text
44
+ if title == "--TOP--"
45
+ file_name = item.parent.attributes["identifierref"].value
46
+ title = item.parent.at("title").text
47
+ item_id = item.parent.attributes["identifierref"].
48
+ value.gsub("res", "")
49
+ else
50
+ file_name = item.attributes["identifierref"].value
51
+ if parent_item.attributes["identifierref"]
52
+ item_id = parent_item.attributes["identifierref"].
53
+ value.gsub("res", "")
54
+ else
55
+ item_id = item.attributes["identifierref"].value.gsub("res", "")
56
+ end
57
+ end
58
+ toc_item = course_toc.
59
+ detect { |ct| ct[:original_file] == file_name } || {}
60
+ toc_item[:file_name] = file_name
61
+ toc_item[:title] = title
62
+ toc_item[:parent_id] = get_parent_id(course_toc, item_id)
63
+ toc_item
64
+ end
65
+ end
66
+
67
+ def self.get_parent_id(course_toc, item_id)
68
+ header_ids = get_headers(course_toc, "SUBHEADER")
69
+ if header_ids.empty?
70
+ header_ids = get_headers(course_toc, "CONTENT")
71
+ end
72
+ header_id = header_ids.
73
+ reject { |x| x.to_i > item_id.to_i }.
74
+ min_by { |x| (x.to_i - item_id.to_i).abs }
75
+
76
+ header_id ? "res#{header_id}" : nil
77
+ end
78
+
79
+ def self.get_headers(course_toc, target_type)
80
+ course_toc.select { |ct| ct[:target_type] == target_type }.
81
+ map { |sh| sh[:original_file].gsub("res", "") }
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,15 @@
1
+ module Senkyoshi
2
+ ##
3
+ # Represents a link between Blackboard resources.
4
+ ##
5
+ class Link < FileResource
6
+ def self.get_pre_data(xml_data, _file)
7
+ {
8
+ referrer: xml_data.children.at("REFERRER").attributes["id"].value,
9
+ referred_to: xml_data.children.at("REFERREDTO").attributes["id"].value,
10
+ referred_to_title: xml_data.children.at("TITLE").
11
+ attributes["value"].value,
12
+ }
13
+ end
14
+ end
15
+ end
@@ -10,6 +10,12 @@ module Senkyoshi
10
10
  @module_items = []
11
11
  end
12
12
 
13
+ def self.find_module_from_item_id(modules, id)
14
+ modules.detect do |mod|
15
+ mod.module_items.detect { |item| item.identifierref == id }
16
+ end
17
+ end
18
+
13
19
  def canvas_conversion(*)
14
20
  CanvasCc::CanvasCC::Models::CanvasModule.new.tap do |cc_module|
15
21
  cc_module.identifier = @identifier
@@ -0,0 +1,86 @@
1
+ module Senkyoshi
2
+ class ModuleConverter
3
+ def self.set_modules(course, pre_data)
4
+ master_module = Course.master_module(course)
5
+ subheaders = get_subheaders(pre_data)
6
+ pre_data.each do |data|
7
+ if check_module_header(data, subheaders)
8
+ course = add_canvas_module(course, data)
9
+ elsif data[:target_type] == "CONTENT"
10
+ module_item = create_module_subheader(data)
11
+ course = add_canvas_module_item(course, module_item, data)
12
+ elsif data[:target_type] == nil || data[:target_type] == "APPLICATION"
13
+ module_item = master_module.module_items.
14
+ detect { |i| i.identifier == data[:file_name] }
15
+ if module_item
16
+ course = add_canvas_module_item(course, module_item, data)
17
+ end
18
+ end
19
+ end
20
+ course.canvas_modules.delete(master_module)
21
+ course
22
+ end
23
+
24
+ def self.check_module_header(data, subheaders)
25
+ data[:target_type] == "SUBHEADER" || data[:target_type] == "CONTENT" &&
26
+ (subheaders.empty? || !data[:parent_id])
27
+ end
28
+
29
+ def self.get_subheaders(pre_data)
30
+ pre_data.select { |ct| ct[:target_type] == "SUBHEADER" }.
31
+ map { |sh| sh[:original_file].gsub("res", "") }
32
+ end
33
+
34
+ def self.create_module_subheader(data)
35
+ ModuleItem.new(
36
+ data[:title],
37
+ "ContextModuleSubHeader",
38
+ data[:file_name],
39
+ nil,
40
+ data[:indent],
41
+ data[:file_name],
42
+ ).canvas_conversion
43
+ end
44
+
45
+ def self.add_canvas_module(course, data)
46
+ canvas_module = Module.new(data[:title], data[:file_name])
47
+ course.canvas_modules << canvas_module.canvas_conversion
48
+ course
49
+ end
50
+
51
+ def self.add_canvas_module_item(course, module_item, data)
52
+ parent_module = get_subheader_parent(course, data)
53
+ if !parent_module
54
+ parent_module = Module.new(course.title, MAIN_CANVAS_MODULE).
55
+ canvas_conversion
56
+ course.canvas_modules << parent_module
57
+ end
58
+ parent_module.module_items ||= {}
59
+ parent_module.module_items << module_item
60
+ course
61
+ end
62
+
63
+ def self.get_subheader_parent(course, data)
64
+ if !data[:parent_id]
65
+ parent_module = course.canvas_modules.
66
+ detect { |a| a.identifier == MAIN_CANVAS_MODULE }
67
+ else
68
+ parent_module = course.canvas_modules.
69
+ detect { |a| a.identifier == data[:parent_id] }
70
+ if !parent_module
71
+ course.canvas_modules.
72
+ reject { |a| a.title == MASTER_MODULE }.
73
+ each do |cc_module|
74
+ if cc_module.module_items
75
+ items = cc_module.module_items.flatten
76
+ item = items.
77
+ detect { |i| i.identifier == data[:parent_id] }
78
+ parent_module = cc_module if item
79
+ end
80
+ end
81
+ end
82
+ end
83
+ parent_module
84
+ end
85
+ end
86
+ end
@@ -2,15 +2,20 @@ require "senkyoshi/models/resource"
2
2
 
3
3
  module Senkyoshi
4
4
  class ModuleItem < Resource
5
- def initialize(title, type, identifierref, url)
5
+ def initialize(title, type, identifierref, url, indent, id)
6
6
  @title = title
7
- @identifier = Senkyoshi.create_random_hex
7
+ @identifier = id || Senkyoshi.create_random_hex
8
8
  @content_type = type
9
9
  @identifierref = identifierref
10
+ @indent = indent
10
11
  @workflow_state = "active"
11
12
  @url = url
12
13
  end
13
14
 
15
+ def self.find_item_from_id_ref(module_items, id_ref)
16
+ module_items.detect { |item| item.identifierref == id_ref }
17
+ end
18
+
14
19
  def canvas_conversion(*)
15
20
  CanvasCc::CanvasCC::Models::ModuleItem.new.tap do |item|
16
21
  item.title = @title
@@ -18,6 +23,7 @@ module Senkyoshi
18
23
  item.content_type = @content_type
19
24
  item.identifierref = @identifierref
20
25
  item.workflow_state = @workflow_state
26
+ item.indent = @indent
21
27
  item.url = @url
22
28
  end
23
29
  end
@@ -5,12 +5,15 @@ module Senkyoshi
5
5
  class OutcomeDefinition < Resource
6
6
  include Senkyoshi
7
7
  attr_reader :id, :content_id, :asidataid, :is_user_created
8
- def self.from(xml, category)
9
- outcome_definition = OutcomeDefinition.new(category)
8
+ def self.from(xml, category, id = nil)
9
+ outcome_definition = OutcomeDefinition.new(category, id)
10
10
  outcome_definition.iterate_xml(xml)
11
11
  end
12
12
 
13
- def initialize(category)
13
+ def initialize(category, id, content_id = nil, asidataid = nil)
14
+ @id = id
15
+ @content_id = content_id
16
+ @asidataid = asidataid
14
17
  @category = category
15
18
  end
16
19
 
@@ -20,7 +23,9 @@ module Senkyoshi
20
23
  @id = xml.xpath("./@id").text
21
24
  @title = xml.xpath("./TITLE/@value").text
22
25
  @points_possible = xml.xpath("./POINTSPOSSIBLE/@value").text
23
- @is_user_created = true? xml.xpath("./ISUSERCREATED/@value").text
26
+ @is_user_created = Senkyoshi.true?(
27
+ xml.xpath("./ISUSERCREATED/@value").text,
28
+ )
24
29
  self
25
30
  end
26
31
 
@@ -37,7 +42,7 @@ module Senkyoshi
37
42
  def canvas_conversion(course, _ = nil)
38
43
  assignment_group = AssignmentGroup.find_or_create(course, @category)
39
44
  assignment = CanvasCc::CanvasCC::Models::Assignment.new
40
- assignment.identifier = Senkyoshi.create_random_hex
45
+ assignment.identifier = @id
41
46
  assignment.assignment_group_identifier_ref = assignment_group.identifier
42
47
  assignment.title = @title
43
48
  assignment.position = 1
@@ -11,6 +11,7 @@ QTI_TYPE = {
11
11
 
12
12
  module Senkyoshi
13
13
  class QTI < FileResource
14
+ attr_reader(:points_possible)
14
15
  def self.from(data, pre_data, _resource_xids = nil)
15
16
  type = data.at("bbmd_assessmenttype").text
16
17
  qti_class = Senkyoshi.const_get QTI_TYPE[type]
@@ -102,7 +102,7 @@ module Senkyoshi
102
102
  end
103
103
 
104
104
  def get_fraction(answer_text)
105
- if @correct_answers && answer_text == @correct_answers["name"]
105
+ if @correct_answers && answer_text.to_s == @correct_answers["name"].to_s
106
106
  @correct_answers["fraction"].to_f
107
107
  else
108
108
  @incorrect_answers["fraction"].to_f
@@ -125,7 +125,8 @@ module Senkyoshi
125
125
  if score_number > 0
126
126
  @correct_answers["fraction"] = score_number.to_f / @max_score.to_f
127
127
  else
128
- @correct_answers["fraction"] = 0
128
+ # mark as correct when there is no score for the answer
129
+ @correct_answers["fraction"] = 1
129
130
  end
130
131
  end
131
132
  end
@@ -51,7 +51,11 @@ module Senkyoshi
51
51
  end
52
52
 
53
53
  answers << answer
54
- @matches << { id: id, question_text: question, answer_text: answer }
54
+ @matches << {
55
+ id: Senkyoshi.create_random_hex,
56
+ question_text: question,
57
+ answer_text: answer,
58
+ }
55
59
  end
56
60
  end
57
61
  @distractors = matches_array.reject { |i| answers.include?(i) }
@@ -32,9 +32,11 @@ module Senkyoshi
32
32
  def set_order_answers(resprocessing)
33
33
  order_answers = {}
34
34
  correct = resprocessing.at("respcondition[title=correct]")
35
- correct.at("and").children.each_with_index do |varequal, index|
36
- id = varequal.text
37
- order_answers[id] = index + 1
35
+ if correct
36
+ correct.at("and").children.each_with_index do |varequal, index|
37
+ id = varequal.text
38
+ order_answers[id] = index + 1
39
+ end
38
40
  end
39
41
  order_answers
40
42
  end
@@ -0,0 +1,46 @@
1
+ require "senkyoshi/models/grade_completed_criteria"
2
+ require "senkyoshi/models/content_reviewed_criteria"
3
+ require "senkyoshi/models/grade_range_criteria"
4
+ require "senkyoshi/models/grade_range_percent_criteria"
5
+ require "senkyoshi/models/resource"
6
+
7
+ module Senkyoshi
8
+ class Rule < FileResource
9
+ attr_reader(:title, :content_id, :criteria_list, :id)
10
+
11
+ CRITERIA_MAP = {
12
+ grade_completed_criteria: GradeCompletedCriteria,
13
+ content_reviewed_criteria: ContentReviewedCriteria,
14
+ grade_range_criteria: GradeRangeCriteria,
15
+ grade_range_percent_criteria: GradeRangePercentCriteria,
16
+ }.freeze
17
+
18
+ def initialize(resource_id)
19
+ super(resource_id)
20
+ @criteria_list = []
21
+ end
22
+
23
+ def get_criteria_list(xml)
24
+ xml.children.select { |child_xml| !child_xml.blank? }.
25
+ map do |child_xml|
26
+ criteria = CRITERIA_MAP[child_xml.name.downcase.to_sym]
27
+ criteria.from_xml(child_xml) if !criteria.nil?
28
+ end.compact
29
+ end
30
+
31
+ def iterate_xml(xml, _pre_data = nil)
32
+ @title = xml.xpath("./TITLE/@value").text
33
+ @content_id = xml.xpath("./CONTENT_ID/@value").text
34
+ @id = xml.xpath("./@id").text
35
+ @criteria_list = get_criteria_list(xml.xpath("./CRITERIA_LIST"))
36
+ self
37
+ end
38
+
39
+ def canvas_conversion(course, _resources)
40
+ @criteria_list.each do |criteria|
41
+ criteria.canvas_conversion(course, @content_id, _resources)
42
+ end
43
+ course
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,146 @@
1
+ require "senkyoshi"
2
+
3
+ module Senkyoshi
4
+ class RuleCriteria
5
+ include Senkyoshi
6
+ attr_reader(:id, :negated, :content_id, :asidata_id)
7
+
8
+ COMPLETION_TYPES = {
9
+ must_view: "must_view",
10
+ must_submit: "must_submit",
11
+ min_score: "min_score",
12
+ must_contribute: "must_contribute",
13
+ }.freeze
14
+
15
+ def initialize(id, negated)
16
+ @id = id
17
+ @negated = negated
18
+ end
19
+
20
+ def add_if_unique(collection, item)
21
+ if collection.detect do |collection_item|
22
+ yield(collection_item, item)
23
+ end.nil?
24
+ collection << item
25
+ end
26
+ collection
27
+ end
28
+
29
+ def add_to_module_if_unique(items, item)
30
+ add_if_unique(
31
+ items, item
32
+ ) { |a, b| a.identifierref == b.identifierref }
33
+ end
34
+
35
+ def self.get_id(xml)
36
+ xml.xpath("./@id").text
37
+ end
38
+
39
+ def self.get_negated(xml)
40
+ xml.xpath("./NEGATED/@value").text
41
+ end
42
+
43
+ def self.from_xml(xml)
44
+ new(RuleCriteria.get_id(xml), RuleCriteria.get_negated(xml))
45
+ end
46
+
47
+ ##
48
+ # Determine two identifierrefs are pointed to by items in the same module
49
+ def self.in_same_module?(modules, content_id, resource_id)
50
+ content_module = Module.find_module_from_item_id(modules, content_id)
51
+ resource_module = Module.find_module_from_item_id(modules, resource_id)
52
+ return nil if content_module.nil? || resource_module.nil?
53
+ content_module == resource_module
54
+ end
55
+
56
+ def self.module_prerequisite?(modules, content_id, resource_id)
57
+ in_same_module?(modules, content_id, resource_id) == false
58
+ end
59
+
60
+ def self.module_completion_requirement?(modules, content_id, resource_id)
61
+ in_same_module?(modules, content_id, resource_id) == true
62
+ end
63
+
64
+ ##
65
+ # Returns the most applicable id. If we have an assignment id return that,
66
+ # otherwise return the content id.
67
+ ##
68
+ def get_id
69
+ @asidata_id || @content_id
70
+ end
71
+
72
+ ##
73
+ # Factory method to construct a completion requirement. Should be passed
74
+ # the module that the completion requirement should belong to
75
+ ##
76
+ def make_completion(mod)
77
+ CanvasCc::CanvasCC::Models::ModuleCompletionRequirement.new.tap do |req|
78
+ mod_item = ModuleItem.find_item_from_id_ref(
79
+ mod.module_items, get_foreign_id
80
+ )
81
+
82
+ req.identifierref = mod_item.identifier if mod_item
83
+ req.type = get_completion_type
84
+ end
85
+ end
86
+
87
+ ##
88
+ # Factory method to construct a module prerequisite. Should be passed the
89
+ # prerequisite module
90
+ ##
91
+ def make_prereq(prereq_module)
92
+ CanvasCc::CanvasCC::Models::ModulePrerequisite.new.tap do |prereq|
93
+ prereq.identifierref = prereq_module.identifier
94
+ prereq.type = "context_module"
95
+ end
96
+ end
97
+
98
+ ##
99
+ # Use gradebook to find assignment id if applicable
100
+ ##
101
+ def set_ids(content_id, resources)
102
+ @content_id = content_id
103
+ gradebook = resources.find_instances_of(Gradebook).first
104
+ if gradebook
105
+ outcome_def = gradebook.outcome_definitions.detect do |out_def|
106
+ out_def.content_id == content_id
107
+ end
108
+ @asidata_id = outcome_def.asidataid if outcome_def
109
+ end
110
+ end
111
+
112
+ def canvas_conversion(course, content_id, resources)
113
+ set_ids(content_id, resources)
114
+
115
+ mod = Module.find_module_from_item_id course.canvas_modules, get_id
116
+
117
+ is_completion = RuleCriteria.module_completion_requirement?(
118
+ course.canvas_modules, get_id, get_foreign_id
119
+ )
120
+
121
+ is_prereq = RuleCriteria.module_prerequisite?(
122
+ course.canvas_modules, get_id, get_foreign_id
123
+ )
124
+
125
+ if is_completion
126
+ add_to_module_if_unique(
127
+ mod.completion_requirements, make_completion(mod)
128
+ )
129
+ elsif is_prereq
130
+ prereq_module = Module.find_module_from_item_id(
131
+ course.canvas_modules, get_foreign_id
132
+ )
133
+
134
+ add_to_module_if_unique(
135
+ mod.prerequisites, make_prereq(prereq_module)
136
+ )
137
+
138
+ add_to_module_if_unique(
139
+ prereq_module.completion_requirements, make_completion(prereq_module)
140
+ )
141
+ end
142
+
143
+ course
144
+ end
145
+ end
146
+ end
@@ -35,6 +35,7 @@ module Senkyoshi
35
35
 
36
36
  def _set_body(original_body, url, extendeddata)
37
37
  body = original_body.dup
38
+
38
39
  if !url.empty?
39
40
  body = %{
40
41
  <a href="#{url}">
@@ -43,12 +44,21 @@ module Senkyoshi
43
44
  #{body}
44
45
  }
45
46
  end
47
+ if @referred_to_title.present?
48
+ body = %{
49
+ <a href="$CANVAS_COURSE_REFERENCE$#{@referred_to_title}">
50
+ Course Link: #{@referred_to_title}
51
+ </a>
52
+ #{body}
53
+ }
54
+ end
46
55
  if extendeddata
47
56
  body = %{
48
57
  #{body}
49
58
  #{_extendeddata(extendeddata)}
50
59
  }
51
60
  end
61
+
52
62
  body
53
63
  end
54
64
 
@@ -63,10 +73,10 @@ module Senkyoshi
63
73
  end
64
74
 
65
75
  def _component_label(node)
66
- visible = true?(node.search("vislableToStudents/@value").to_s)
76
+ visible = Senkyoshi.true?(node.search("vislableToStudents/@value").to_s)
67
77
  if visible
68
78
  component_label = node.search("componentLabel/@value").to_s
69
- overridden = true?(node.search("labelOverridden/@value").to_s)
79
+ overridden = Senkyoshi.true?(node.search("labelOverridden/@value").to_s)
70
80
  if overridden
71
81
  component_label
72
82
  else
@@ -1,3 +1,3 @@
1
1
  module Senkyoshi
2
- VERSION = "1.0.3".freeze
2
+ VERSION = "1.0.4".freeze
3
3
  end
@@ -20,21 +20,27 @@ require "senkyoshi/models/assessment"
20
20
  require "senkyoshi/models/question_bank"
21
21
  require "senkyoshi/models/survey"
22
22
 
23
+ require "senkyoshi/models/heirarchy"
24
+
23
25
  require "senkyoshi/models/announcement"
24
26
  require "senkyoshi/models/answer"
25
- require "senkyoshi/models/qti"
26
27
  require "senkyoshi/models/assignment"
27
28
  require "senkyoshi/models/assignment_group"
29
+ require "senkyoshi/models/attachment"
28
30
  require "senkyoshi/models/blog"
29
31
  require "senkyoshi/models/content"
30
32
  require "senkyoshi/models/content_file"
31
33
  require "senkyoshi/models/course"
34
+ require "senkyoshi/models/course_toc"
35
+ require "senkyoshi/models/external_url"
32
36
  require "senkyoshi/models/file"
33
37
  require "senkyoshi/models/forum"
34
38
  require "senkyoshi/models/gradebook"
35
39
  require "senkyoshi/models/group"
40
+ require "senkyoshi/models/link"
36
41
  require "senkyoshi/models/module"
37
42
  require "senkyoshi/models/module_item"
43
+ require "senkyoshi/models/qti"
38
44
  require "senkyoshi/models/question"
39
45
  require "senkyoshi/models/quiz"
40
46
  require "senkyoshi/models/resource"
@@ -43,7 +49,7 @@ require "senkyoshi/models/staff_info"
43
49
  require "senkyoshi/models/wikipage"
44
50
  require "senkyoshi/models/attachment"
45
51
  require "senkyoshi/models/external_url"
46
-
52
+ require "senkyoshi/models/rule"
47
53
  require "senkyoshi/exceptions"
48
54
 
49
55
  module Senkyoshi
@@ -57,28 +63,21 @@ module Senkyoshi
57
63
  content: "Content",
58
64
  staffinfo: "StaffInfo",
59
65
  gradebook: "Gradebook",
66
+ rule: "Rule",
60
67
  }.freeze
61
68
 
62
69
  PRE_RESOURCE_TYPE = {
63
- content: "Content",
70
+ coursetoc: "CourseToc",
64
71
  gradebook: "Gradebook",
72
+ link: "Link",
65
73
  courseassessment: "QTI",
66
74
  }.freeze
67
75
 
68
- def self.parse_manifest(zip_file, manifest, resource_xids)
69
- doc = Nokogiri::XML.parse(manifest)
70
- resources = doc.at("resources")
71
- organizations = doc.at("organizations")
72
- iterate_xml(organizations, resources, zip_file, resource_xids).
73
- flatten - ["", nil]
74
- end
75
-
76
- def self.iterate_xml(organizations, resources, zip_file, resource_xids)
77
- pre_data = pre_iterator(organizations, resources, zip_file)
76
+ def self.iterate_xml(resources, zip_file, resource_xids, pre_data)
78
77
  staff_info = StaffInfo.new
79
78
  iterator_master(resources, zip_file) do |xml_data, type, file|
80
79
  if RESOURCE_TYPE[type.to_sym]
81
- single_pre_data = get_single_pre_data(pre_data, file)
80
+ single_pre_data = get_single_pre_data(pre_data, file) || {}
82
81
  res_class = Senkyoshi.const_get RESOURCE_TYPE[type.to_sym]
83
82
  case type
84
83
  when "staffinfo"
@@ -87,12 +86,12 @@ module Senkyoshi
87
86
  res_class.from(xml_data, single_pre_data, resource_xids)
88
87
  end
89
88
  end
90
- end
89
+ end.flatten - ["", nil]
91
90
  end
92
91
 
93
92
  def self.get_single_pre_data(pre_data, file)
94
- pre_data.detect do |d|
95
- d[:file_name] == file || d[:assignment_id] == file
93
+ pre_data.detect do |data_item|
94
+ data_item[:file_name] == file || data_item[:assignment_id] == file
96
95
  end || { file_name: file }
97
96
  end
98
97
 
@@ -119,8 +118,9 @@ module Senkyoshi
119
118
  pre_data[type].push(data) if data
120
119
  end
121
120
  end
121
+ pre_data["content"] = build_heirarchy(organizations, resources,
122
+ pre_data["coursetoc"]) - ["", nil]
122
123
  pre_data = connect_content(pre_data)
123
- build_heirarchy(organizations, pre_data)
124
124
  end
125
125
 
126
126
  def self.connect_content(pre_data)
@@ -130,39 +130,33 @@ module Senkyoshi
130
130
  detect { |g| g[:content_id] == content[:file_name] }
131
131
  content.merge!(gradebook) if gradebook
132
132
  end
133
+
133
134
  if pre_data["courseassessment"]
134
135
  course_assessment = pre_data["courseassessment"].
135
136
  detect { |ca| ca[:original_file_name] == content[:assignment_id] }
136
137
  content.merge!(course_assessment) if course_assessment
137
138
  end
138
- end
139
- pre_data["content"]
140
- end
141
139
 
142
- def self.build_heirarchy(organizations, pre_data)
143
- unset_id = "{unset id}"
144
- parents = pre_data.
145
- select { |p| p[:parent_id] == unset_id }
146
- parents_ids = parents.map { |u| u[:id] }
147
- pre_data.each do |content|
148
- parent_id = content[:parent_id]
149
- parent = pre_data.detect { |p| p[:id] == parent_id }
150
- if parent_id == unset_id
151
- content[:title] = get_title(organizations, content)
152
- elsif parent[:parent_id] == unset_id
153
- content[:parent_title] = parent[:title]
140
+ if pre_data["link"]
141
+ matching_link = pre_data["link"].detect do |link|
142
+ link[:referrer] == content[:file_name]
143
+ end
144
+
145
+ if matching_link
146
+ content[:referred_to_title] = matching_link[:referred_to_title]
147
+ end
154
148
  end
155
- next if parents_ids.include?(content[:id])
156
- next if parents_ids.include?(parent_id)
157
- parents_ids << parent_id
158
- parent[:parent_id] = parent[:id]
159
- parent[:parent_title] = nil
160
149
  end
150
+
151
+ pre_data["content"]
161
152
  end
162
153
 
163
- def self.get_title(organizations, content)
164
- item = organizations.at("item[@identifierref=#{content[:file_name]}]")
165
- item.parent.at("title").text
154
+ def self.build_heirarchy(organizations, resources, course_toc)
155
+ discussion_boards = resources.
156
+ search("resource[type=\"resource/x-bb-discussionboard\"]")
157
+ organizations.at("organization").children.flat_map do |item|
158
+ Heirarchy.item_iterator(item, course_toc, discussion_boards)
159
+ end
166
160
  end
167
161
 
168
162
  ##
data/lib/senkyoshi.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require "senkyoshi/version"
2
2
  require "senkyoshi/xml_parser"
3
+ require "senkyoshi/models/module_converter"
3
4
  require "senkyoshi/canvas_course"
4
5
  require "senkyoshi/collection"
5
6
  require "senkyoshi/configuration"
@@ -15,6 +16,8 @@ require "senkyoshi/exceptions"
15
16
  module Senkyoshi
16
17
  FILE_BASE = "$IMS-CC-FILEBASE$".freeze
17
18
  DIR_BASE = "$CANVAS_COURSE_REFERENCE$/files/folder".freeze
19
+ MAIN_CANVAS_MODULE = "aj_main_module".freeze
20
+ MASTER_MODULE = "master_module".freeze
18
21
 
19
22
  class << self
20
23
  attr_writer :configuration
@@ -41,9 +44,17 @@ module Senkyoshi
41
44
  resource_xids = resources.resources.
42
45
  map(&:xid).
43
46
  select { |r| r.include?("xid-") }
44
- resources.add(Senkyoshi.parse_manifest(file, manifest, resource_xids))
45
47
 
46
- course = create_canvas_course(resources, zip_path)
48
+ xml = Nokogiri::XML.parse(manifest)
49
+ xml_resources = xml.at("resources")
50
+ xml_organizations = xml.at("organizations")
51
+
52
+ pre_data = Senkyoshi.pre_iterator(xml_organizations, xml_resources, file)
53
+ resources.add(
54
+ Senkyoshi.iterate_xml(xml_resources, file, resource_xids, pre_data),
55
+ )
56
+
57
+ course = create_canvas_course(resources, zip_path, pre_data)
47
58
  build_file(course, imscc_path, resources)
48
59
  end
49
60
  end
@@ -73,12 +84,21 @@ module Senkyoshi
73
84
  resources.each(&:cleanup)
74
85
  end
75
86
 
76
- def self.create_canvas_course(resources, zip_name)
87
+ def self.create_canvas_course(resources, zip_name, pre_data)
77
88
  course = CanvasCc::CanvasCC::Models::Course.new
78
89
  course.course_code = zip_name
79
- resources.each do |resource|
90
+
91
+ # Wait until after we set modules to convert Rules
92
+ resources.find_instances_not_of([Rule]).each do |resource|
80
93
  course = resource.canvas_conversion(course, resources)
81
94
  end
95
+
96
+ course = ModuleConverter.set_modules(course, pre_data)
97
+
98
+ resources.find_instances_of(Rule).each do |rule|
99
+ course = rule.canvas_conversion(course, resources)
100
+ end
101
+
82
102
  course
83
103
  end
84
104
 
@@ -91,7 +111,7 @@ module Senkyoshi
91
111
  end
92
112
  end
93
113
 
94
- def true?(obj)
114
+ def self.true?(obj)
95
115
  obj.to_s == "true"
96
116
  end
97
117
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: senkyoshi
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Atomic Jolt
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-01-23 00:00:00.000000000 Z
11
+ date: 2017-01-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry-byebug
@@ -177,14 +177,23 @@ files:
177
177
  - lib/senkyoshi/models/blog.rb
178
178
  - lib/senkyoshi/models/content.rb
179
179
  - lib/senkyoshi/models/content_file.rb
180
+ - lib/senkyoshi/models/content_reviewed_criteria.rb
180
181
  - lib/senkyoshi/models/course.rb
182
+ - lib/senkyoshi/models/course_toc.rb
181
183
  - lib/senkyoshi/models/external_url.rb
182
184
  - lib/senkyoshi/models/file.rb
183
185
  - lib/senkyoshi/models/file_resource.rb
184
186
  - lib/senkyoshi/models/forum.rb
187
+ - lib/senkyoshi/models/grade_completed_criteria.rb
188
+ - lib/senkyoshi/models/grade_criteria.rb
189
+ - lib/senkyoshi/models/grade_range_criteria.rb
190
+ - lib/senkyoshi/models/grade_range_percent_criteria.rb
185
191
  - lib/senkyoshi/models/gradebook.rb
186
192
  - lib/senkyoshi/models/group.rb
193
+ - lib/senkyoshi/models/heirarchy.rb
194
+ - lib/senkyoshi/models/link.rb
187
195
  - lib/senkyoshi/models/module.rb
196
+ - lib/senkyoshi/models/module_converter.rb
188
197
  - lib/senkyoshi/models/module_item.rb
189
198
  - lib/senkyoshi/models/outcome_definition.rb
190
199
  - lib/senkyoshi/models/qti.rb
@@ -209,6 +218,8 @@ files:
209
218
  - lib/senkyoshi/models/questions/true_false.rb
210
219
  - lib/senkyoshi/models/quiz.rb
211
220
  - lib/senkyoshi/models/resource.rb
221
+ - lib/senkyoshi/models/rule.rb
222
+ - lib/senkyoshi/models/rule_criteria.rb
212
223
  - lib/senkyoshi/models/scorm_package.rb
213
224
  - lib/senkyoshi/models/staff_info.rb
214
225
  - lib/senkyoshi/models/survey.rb
@@ -236,7 +247,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
236
247
  version: '0'
237
248
  requirements: []
238
249
  rubyforge_project:
239
- rubygems_version: 2.4.8
250
+ rubygems_version: 2.5.1
240
251
  signing_key:
241
252
  specification_version: 4
242
253
  summary: Converts Blackboard zip file to Canvas Common Cartridge