common_cartridge_parser 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/lib/common_cartridge.rb +40 -0
  3. data/lib/common_cartridge/elements/lom.rb +77 -0
  4. data/lib/common_cartridge/elements/manifest.rb +30 -0
  5. data/lib/common_cartridge/elements/metadata.rb +12 -0
  6. data/lib/common_cartridge/elements/organizations.rb +40 -0
  7. data/lib/common_cartridge/elements/outcomes.rb +10 -0
  8. data/lib/common_cartridge/elements/outcomes/outcome.rb +15 -0
  9. data/lib/common_cartridge/elements/outcomes/outcome_group.rb +12 -0
  10. data/lib/common_cartridge/elements/outcomes/outcome_root.rb +11 -0
  11. data/lib/common_cartridge/elements/resources.rb +119 -0
  12. data/lib/common_cartridge/elements/resources/assessment.rb +21 -0
  13. data/lib/common_cartridge/elements/resources/assignment.rb +59 -0
  14. data/lib/common_cartridge/elements/resources/attachments.rb +20 -0
  15. data/lib/common_cartridge/elements/resources/basic_lti_link.rb +66 -0
  16. data/lib/common_cartridge/elements/resources/page.rb +22 -0
  17. data/lib/common_cartridge/elements/resources/topic.rb +28 -0
  18. data/lib/common_cartridge/elements/resources/web_link.rb +25 -0
  19. data/lib/common_cartridge/package.rb +87 -0
  20. data/lib/common_cartridge/parsers/dependencies.rb +23 -0
  21. data/lib/common_cartridge/parsers/files.rb +34 -0
  22. data/lib/common_cartridge/parsers/items.rb +58 -0
  23. data/lib/common_cartridge/parsers/outcomes.rb +17 -0
  24. data/lib/common_cartridge/parsers/parser.rb +84 -0
  25. data/lib/common_cartridge/parsers/questions.rb +34 -0
  26. data/lib/common_cartridge/version.rb +3 -0
  27. data/spec/elements/lom_spec.rb +66 -0
  28. data/spec/elements/manifest_spec.rb +31 -0
  29. data/spec/elements/metadata_spec.rb +40 -0
  30. data/spec/elements/organizations_spec.rb +51 -0
  31. data/spec/elements/outcomes/outcome_group_spec.rb +29 -0
  32. data/spec/elements/outcomes/outcome_spec.rb +37 -0
  33. data/spec/elements/resources/assignment_spec.rb +53 -0
  34. data/spec/elements/resources/attachments_spec.rb +24 -0
  35. data/spec/elements/resources/basic_lti_link_spec.rb +29 -0
  36. data/spec/elements/resources/resources_spec.rb +73 -0
  37. data/spec/elements/resources/topic_spec.rb +39 -0
  38. data/spec/elements/resources/web_link_spec.rb +31 -0
  39. data/spec/files/canvas_large_1.3.imscc +0 -0
  40. data/spec/files/canvas_small_1.1.corrupt.imscc +0 -0
  41. data/spec/files/canvas_small_1.1.imscc +0 -0
  42. data/spec/package_spec.rb +110 -0
  43. data/spec/parser_spec.rb +13 -0
  44. data/spec/spec_helper.rb +13 -0
  45. metadata +190 -0
@@ -0,0 +1,20 @@
1
+ module CommonCartridge
2
+ module Elements
3
+ module Resources
4
+ module Attachments
5
+ class Attachment
6
+ include SAXMachine
7
+
8
+ attribute :href
9
+ attribute :role
10
+ end
11
+
12
+ class RootAttachment
13
+ include SAXMachine
14
+
15
+ elements :'dt:attachment', class: Attachment, as: :attachments
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,66 @@
1
+ module CommonCartridge
2
+ module Elements
3
+ module Resources
4
+ module BasicLtiLink
5
+ class VendorContact
6
+ include SAXMachine
7
+
8
+ element 'lticp:email', as: :email
9
+ end
10
+
11
+ class Vendor
12
+ include SAXMachine
13
+
14
+ element 'lticp:code', as: :code
15
+ element 'lticp:name', as: :name
16
+ element 'lticp:description', as: :description
17
+ element 'lticp:url', as: :url
18
+ element 'lticp:contact', class: VendorContact, as: :contact
19
+ end
20
+
21
+ class ExtensionProperty
22
+ include SAXMachine
23
+
24
+ attribute :name
25
+ value :value
26
+ end
27
+
28
+ class Extension
29
+ include SAXMachine
30
+
31
+ attribute :platform
32
+ elements 'lticm:property', class: ExtensionProperty, as: :properties
33
+ end
34
+
35
+ class BasicLtiLink
36
+ attr_accessor :identifier
37
+
38
+ include SAXMachine
39
+
40
+ attribute :xmlns
41
+ attribute 'xmlns:blti', as: :xmlns_blti
42
+ attribute 'xmlns:lticm', as: :xmlns_lticm
43
+ attribute 'xmlns:lticp', as: :xmlns_lticp
44
+ attribute 'xmlns:xsi', as: :xmlns_xsi
45
+ attribute 'xsi:schemaLocation', as: :xsi_schema_location
46
+
47
+ element 'blti:title', as: :title
48
+ element 'blti:description', as: :description
49
+ element 'blti:secure_launch_url', as: :secure_launch_url
50
+ element 'blti:launch_url', as: :launch_url
51
+ element 'blti:vendor', class: Vendor, as: :vendor
52
+
53
+ elements 'blti:extensions', class: Extension, as: :extensions
54
+
55
+ def self.type
56
+ :basic_lti_link
57
+ end
58
+
59
+ def self.pattern
60
+ /imsbasiclti_xmlv1p/
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,22 @@
1
+ module CommonCartridge
2
+ module Elements
3
+ module Resources
4
+ class Page
5
+ attr_accessor :identifier, :identifierref
6
+
7
+ include SAXMachine
8
+ element :title
9
+ element :text
10
+ element :text, value: :texttype, as: :text_type
11
+
12
+ def self.type
13
+ :page
14
+ end
15
+
16
+ def self.pattern
17
+ /webcontent/
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,28 @@
1
+ module CommonCartridge
2
+ module Elements
3
+ module Resources
4
+ class Topic
5
+ attr_accessor :identifier
6
+
7
+ include SAXMachine
8
+
9
+ element 'dt:title', as: :title
10
+ element 'dt:text', as: :text
11
+ element 'dt:text', value: :texttype, as: :text_type
12
+ element 'dt:attachments', class: Attachments::RootAttachment, as: :attachment_root
13
+
14
+ def attachments
15
+ attachment_root.attachments
16
+ end
17
+
18
+ def self.type
19
+ :discussion
20
+ end
21
+
22
+ def self.pattern
23
+ /imsdt/
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,25 @@
1
+ module CommonCartridge
2
+ module Elements
3
+ module Resources
4
+ class WebLink
5
+ attr_accessor :identifier
6
+
7
+ include SAXMachine
8
+
9
+ element :title
10
+ element :url
11
+ element :url, value: :href, as: :href
12
+ element :url, value: :target, as: :target
13
+ element :url, value: :windoFeatures, as: :window_features
14
+
15
+ def self.type
16
+ :weblink
17
+ end
18
+
19
+ def self.pattern
20
+ /wl/
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,87 @@
1
+ module CommonCartridge
2
+ class Package
3
+ attr_accessor :manifest, :outcomes, :assignments
4
+
5
+ def initialize
6
+ @assignments = []
7
+ end
8
+
9
+ def find_resource(identifier)
10
+ manifest.resources.find { |r| r.identifier == identifier }
11
+ end
12
+
13
+ def outcome_groups
14
+ @outcome_groups ||= outcomes ? outcomes.outcome_groups : []
15
+ end
16
+
17
+ def all_outcomes
18
+ @all_outcomes ||= outcome_groups.collect { |g| g.outcomes }.flatten
19
+ end
20
+
21
+ def modules
22
+ @modules ||= manifest.organization.root_item.items
23
+ end
24
+
25
+ def discussions
26
+ @discussions ||= resources_of_type(Elements::Resources::Topic).reject { |d| d.is_announcement? }
27
+ end
28
+
29
+ def announcements
30
+ @announcements ||= resources_of_type(Elements::Resources::Topic).select { |d| d.is_announcement? }
31
+ end
32
+
33
+ def pages
34
+ @pages ||= begin
35
+ pages = resources_of_type(Elements::Resources::Page).each do |page|
36
+ assign_title(page)
37
+ end
38
+ filter_pages(pages)
39
+ end
40
+ end
41
+
42
+
43
+ def quizzes
44
+ @quizzes ||= resources_of_type(Elements::Resources::Assessment).each do |quiz|
45
+ assign_title(quiz) unless quiz.title
46
+ end
47
+ end
48
+
49
+
50
+ private
51
+ def filter_pages(pages)
52
+ pages.select do |page|
53
+ (page.href =~ /wiki/ || page.href =~ /html$/) && page.title
54
+ end
55
+ end
56
+
57
+ def assign_title(obj)
58
+ if ref = find_item_by_ref(obj.identifier)
59
+ obj.title = ref.title
60
+ end
61
+ end
62
+
63
+ def resources_of_type(type)
64
+ manifest.resources.select { |r| r.type =~ type.pattern }
65
+ end
66
+
67
+ def find_item_by_ref(ref)
68
+ all_items.detect do |item|
69
+ item.identifierref == ref
70
+ end
71
+ end
72
+
73
+ def all_items
74
+ @all_items ||= begin
75
+ modules.each_with_object([]) do |mod, memo|
76
+ memo << mod.items
77
+ mod.items.each do |item|
78
+ memo << item.items if item.items
79
+ end
80
+ end.flatten
81
+ end
82
+ end
83
+
84
+
85
+
86
+ end
87
+ end
@@ -0,0 +1,23 @@
1
+ module CommonCartridge
2
+ module Parsers
3
+ class Dependencies
4
+ attr_reader :resource
5
+
6
+ def initialize(zipfile, resource)
7
+ @zipfile = zipfile
8
+ @resource = resource
9
+ end
10
+
11
+ def parse!(package)
12
+ resource.dependencies.each do |d|
13
+ resource = package.find_resource(d.identifierref)
14
+ resource.files.each do |f|
15
+ Parser.use_file(@zipfile, f.href) do |xml|
16
+ d.contents << CommonCartridge::Elements::Resources::Content.parse(xml)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,34 @@
1
+ module CommonCartridge
2
+ module Parsers
3
+ class Files
4
+ attr_reader :resource
5
+
6
+ def initialize(zipfile, resource)
7
+ @zipfile = zipfile
8
+ @resource = resource
9
+ end
10
+
11
+ def parse!(package)
12
+ if mapping = resource_to_mapping(resource)
13
+ resource.files.each do |f|
14
+ Parser.use_file(@zipfile, f.href) do |xml|
15
+ f.content = mapping.last.parse(xml)
16
+ f.content.identifier = resource.identifier
17
+ if resource.type =~ CommonCartridge::Elements::Resources::Assignment.pattern && f.href =~ /assignment.*xml/ && !(resource.href =~ /course/)
18
+ doc = Nokogiri::XML(xml)
19
+ doc.remove_namespaces!
20
+ f.content.points_possible = doc.xpath('//points_possible').text
21
+ package.assignments << f.content
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ private
29
+ def resource_to_mapping(resource)
30
+ CommonCartridge::Elements::Resources.type_mappings.detect { |regex, klass| resource.type =~ regex }
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,58 @@
1
+ module CommonCartridge
2
+ module Parsers
3
+ class Items
4
+ def initialize(zipfile, mod, resources)
5
+ @zipfile = zipfile
6
+ @mod = mod
7
+ @resources = resources
8
+ end
9
+
10
+ def parse!
11
+ @mod.items.each do |item|
12
+ map_item_to_type(item)
13
+ unless item.items.empty?
14
+ item.items.each { |i| map_item_to_type(i) }
15
+ end
16
+ end
17
+ end
18
+
19
+ private
20
+ def map_item_to_type(item)
21
+ if resource = find_resource_by_ref(item.identifierref)
22
+ if types = Elements::Resources.type_mappings.detect { |pattern, type| resource.type =~ pattern }
23
+ item.type = types.last.type
24
+ else !resource.type.empty?
25
+ item.type = resource.type
26
+ end
27
+
28
+ if item.type == :page && resource.href =~ /web_resources/
29
+ item.type = 'file'
30
+ end
31
+ end
32
+
33
+ if item.type.nil? || item.type.empty?
34
+ Parser.use_file(@zipfile, 'course_settings/module_meta.xml') do |xml|
35
+ doc = Nokogiri::XML(xml)
36
+ doc.remove_namespaces!
37
+ item.type = snake_case(doc.xpath("//item[@identifier = '#{item.identifier}' or @identifier='#{item.identifierref}']/content_type").text)
38
+
39
+ end
40
+ end
41
+ end
42
+
43
+ def find_resource_by_ref(ref)
44
+ @resources.detect do |resource|
45
+ resource.identifier == ref
46
+ end
47
+ end
48
+
49
+ def snake_case(text)
50
+ text.gsub(/::/, '/').
51
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
52
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
53
+ tr("-", "_").
54
+ downcase
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,17 @@
1
+ module CommonCartridge
2
+ module Parsers
3
+ class Outcomes
4
+ def initialize(zipfile)
5
+ @zipfile = zipfile
6
+ end
7
+
8
+ def parse!
9
+ Parser.use_file(@zipfile, 'course_settings/learning_outcomes.xml') do |xml|
10
+ return CommonCartridge::Elements::Outcomes::OutcomeRoot.parse(xml)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+
@@ -0,0 +1,84 @@
1
+ require 'zip'
2
+
3
+ require 'common_cartridge/parsers/dependencies'
4
+ require 'common_cartridge/parsers/files'
5
+ require 'common_cartridge/parsers/outcomes'
6
+ require 'common_cartridge/parsers/questions'
7
+ require 'common_cartridge/parsers/items'
8
+
9
+ module CommonCartridge
10
+ module Parsers
11
+ class Parser
12
+ class ManifestDocument
13
+ include SAXMachine
14
+
15
+ element :manifest, class: CommonCartridge::Elements::Manifest, required: true
16
+ end
17
+
18
+ def self.use_file(zipfile, path)
19
+ Zip::File.open(File.join(CommonCartridge.config.import_directory, zipfile)) do |file|
20
+ f = file.glob(path).first
21
+ unless f.nil?
22
+ yield f.get_input_stream.read
23
+ end
24
+ end
25
+ end
26
+
27
+ def initialize(zipfile)
28
+ @zipfile = zipfile
29
+ @package = Package.new
30
+ end
31
+
32
+ def parse
33
+ Parser.use_file(@zipfile, 'imsmanifest.xml') do |xml|
34
+ @package.manifest = ManifestDocument.parse(xml).manifest
35
+ end
36
+
37
+ parse_content!
38
+ @package.outcomes = parse_outcomes
39
+ parse_questions!
40
+ parse_module_items!
41
+ @package
42
+ end
43
+
44
+ private
45
+ def parse_module_items!
46
+ @package.modules.each do |mod|
47
+ item_parser = Parsers::Items.new(@zipfile, mod, @package.manifest.resources)
48
+ item_parser.parse!
49
+ end
50
+ end
51
+
52
+ def parse_outcomes
53
+ outcomes_parser = Parsers::Outcomes.new(@zipfile)
54
+ outcomes_parser.parse!
55
+ end
56
+
57
+ def parse_content!
58
+ @package.manifest.resources.map do |resource|
59
+ parse_resource! resource
60
+ end
61
+ end
62
+
63
+ def parse_resource! resource
64
+ file_parser = Parsers::Files.new(@zipfile, resource)
65
+ file_parser.parse!(@package)
66
+ dependency_parser = Parsers::Dependencies.new(@zipfile, resource)
67
+ dependency_parser.parse!(@package)
68
+ rescue RuntimeError => e
69
+ Rails.logger.error "CommonCartridge parse error: #{e.to_s}"
70
+ Rails.logger.error e.backtrace
71
+ end
72
+
73
+ def parse_questions!
74
+ @package.quizzes.each do |quiz|
75
+ parser = Parsers::Questions.new(@zipfile, quiz)
76
+ parser.parse!
77
+ end
78
+ end
79
+
80
+ end
81
+ end
82
+ end
83
+
84
+