csv2qti_lumen 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +30 -0
- data/Rakefile +8 -0
- data/bin/csv2qti +11 -0
- data/csv2qti.gemspec +25 -0
- data/lib/csv2qti/course.rb +38 -0
- data/lib/csv2qti/outcome.rb +63 -0
- data/lib/csv2qti/question.rb +81 -0
- data/lib/csv2qti/question_types/matching.rb +47 -0
- data/lib/csv2qti/question_types/multiple_choice.rb +41 -0
- data/lib/csv2qti/question_types/multiple_select.rb +47 -0
- data/lib/csv2qti/version.rb +3 -0
- data/lib/csv2qti.rb +75 -0
- data/test/fixtures/quiz_questions.rb +15 -0
- data/test/matching_test.rb +18 -0
- data/test/multiple_choice_test.rb +22 -0
- data/test/multiple_select_test.rb +18 -0
- data/test/outcome_test.rb +30 -0
- data/test/question_test.rb +23 -0
- metadata +128 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4fb0fcb4afce967be7d76513dff0643f95746063
|
4
|
+
data.tar.gz: 63f48522bb42995a5643fea43efce88c3e0dd07e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ac655316943ea8f2ebda1b036eb8b2ef44dda0e7abf0c20af20b593ecdfb9d77bee48b999460e0f4abaccdcb45d68985966ba740b5b1816ffcdafcf8a1298075
|
7
|
+
data.tar.gz: 48ee7604e9f594e7eb95691fa6efb288b840906dff650ad90313ba2b47eeed83aaf341a09e3ebfce3e5417e1809182b4cddd8a8068fca1155d370cf05438badb
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Lumen Learning
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# Csv2qti
|
2
|
+
|
3
|
+
Converts a specifically-formatted CSV into QTI.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'csv2qti'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install csv2qti
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
|
24
|
+
## Contributing
|
25
|
+
|
26
|
+
1. Fork it ( https://github.com/lumenlearning/csv2qti/fork )
|
27
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
28
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
29
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
30
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/bin/csv2qti
ADDED
data/csv2qti.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'csv2qti/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "csv2qti_lumen"
|
8
|
+
spec.version = Csv2qti::VERSION
|
9
|
+
spec.authors = ["Bracken Mosbacker"]
|
10
|
+
spec.email = ["bracken@lumenlearning.com"]
|
11
|
+
spec.summary = %q{Converts a specifically-formatted CSV into QTI.}
|
12
|
+
spec.homepage = ""
|
13
|
+
spec.license = "MIT"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0")
|
16
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
17
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_runtime_dependency "canvas_cc"
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
spec.add_development_dependency "minitest"
|
25
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'tmpdir'
|
2
|
+
|
3
|
+
module Csv2qti
|
4
|
+
class Course
|
5
|
+
attr_reader :title, :file_path, :primary_outcomes
|
6
|
+
|
7
|
+
def initialize(title, file_path)
|
8
|
+
@title = title
|
9
|
+
@file_path = file_path
|
10
|
+
@primary_outcomes = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_primary_outcome(row)
|
14
|
+
@primary_outcomes << Outcome.new(row)
|
15
|
+
@primary_outcomes.last
|
16
|
+
end
|
17
|
+
|
18
|
+
def generate_cc(filename=nil)
|
19
|
+
filename ||= @title
|
20
|
+
course = CanvasCc::CanvasCC::Models::Course.new
|
21
|
+
course.title = @title
|
22
|
+
course.identifier = rand(1000).to_s
|
23
|
+
|
24
|
+
@primary_outcomes.each do |po|
|
25
|
+
course.assessments << po.generate_assessment
|
26
|
+
end
|
27
|
+
|
28
|
+
dir = Dir.mktmpdir
|
29
|
+
file = CanvasCc::CanvasCC::CartridgeCreator.new(course).create(dir)
|
30
|
+
|
31
|
+
FileUtils.mv(file, "./#{filename.downcase.gsub(' ', '_')}.zip")
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
@primary_outcomes.inject("#{title}\nOutcomes:") { |s, o| s + "\n" + o.to_s }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Csv2qti
|
2
|
+
class Outcome
|
3
|
+
attr_reader :title, :number, :enabling_outcomes, :description, :license, :questions, :guid
|
4
|
+
|
5
|
+
def initialize(row, parent_outcome=nil)
|
6
|
+
@number = row[0]
|
7
|
+
@title = row[1]
|
8
|
+
@description = row[2]
|
9
|
+
@guid = row[3]
|
10
|
+
@license = row[5]
|
11
|
+
@parent_outcome = parent_outcome
|
12
|
+
@enabling_outcomes = []
|
13
|
+
@questions = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_enabling_outcome(row)
|
17
|
+
@enabling_outcomes << Outcome.new(row, self)
|
18
|
+
@enabling_outcomes.last
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_question(row)
|
22
|
+
@questions << Question.generate(row)
|
23
|
+
end
|
24
|
+
|
25
|
+
def is_primary_outcome?
|
26
|
+
!! @parent_outcome
|
27
|
+
end
|
28
|
+
|
29
|
+
def generate_assessment
|
30
|
+
assessment = CanvasCc::CanvasCC::Models::Assessment.new
|
31
|
+
assessment.identifier = rand(10000).to_s
|
32
|
+
assessment.items = []
|
33
|
+
assessment.title = @title
|
34
|
+
add_questions(assessment)
|
35
|
+
|
36
|
+
@enabling_outcomes.each do |eo|
|
37
|
+
g = CanvasCc::CanvasCC::Models::QuestionGroup.new
|
38
|
+
g.title = eo.title
|
39
|
+
g.identifier = rand(10000).to_s
|
40
|
+
g.selection_number = 1
|
41
|
+
g.points_per_item = 1
|
42
|
+
eo.add_questions(g)
|
43
|
+
assessment.items << g
|
44
|
+
end
|
45
|
+
|
46
|
+
assessment
|
47
|
+
end
|
48
|
+
|
49
|
+
def add_questions(group)
|
50
|
+
@questions.each do |q|
|
51
|
+
q.add_to_group(group)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_s
|
56
|
+
if is_primary_outcome?
|
57
|
+
"#{number}: #{title} - #{description} - #{guid} - #{license} - #{questions.count} questions"
|
58
|
+
else
|
59
|
+
"#{number}: #{title} - #{description} - #{guid} - #{license} - #{enabling_outcomes.count} enabling outcomes"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Csv2qti
|
2
|
+
class Question
|
3
|
+
TYPE = 7
|
4
|
+
STEM = 8
|
5
|
+
CORRECT = 9
|
6
|
+
DISTRACTOR_1 = 10
|
7
|
+
DISTRACTOR_2 = 11
|
8
|
+
LICENSE = 12
|
9
|
+
|
10
|
+
def self.generate(row)
|
11
|
+
type = row[TYPE] || ''
|
12
|
+
case type.downcase
|
13
|
+
when ''
|
14
|
+
raise Csv2qti::QuestionParseError, "Thought this row would be a question but its not."
|
15
|
+
when 'mc'
|
16
|
+
MultipleChoice.new(row)
|
17
|
+
when 'mmc'
|
18
|
+
MultipleSelect.new(row)
|
19
|
+
when 'category'
|
20
|
+
raise Csv2qti::QuestionParseError, "unsupported question type: #{row[TYPE]}"
|
21
|
+
when 'matching'
|
22
|
+
Matching.new(row)
|
23
|
+
when 'process'
|
24
|
+
raise Csv2qti::QuestionParseError, "unsupported question type: #{row[TYPE]}"
|
25
|
+
else
|
26
|
+
raise Csv2qti::QuestionParseError, "unknown question type: #{row[TYPE]}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
attr_reader :stem, :license
|
31
|
+
|
32
|
+
def initialize(row)
|
33
|
+
@stem = process_stem(row[STEM])
|
34
|
+
@license = row[LICENSE]
|
35
|
+
end
|
36
|
+
|
37
|
+
# converts image references to html
|
38
|
+
# images look like this in the original value:
|
39
|
+
# (image: https://s3-us-west-2.amazonaws.com/textimgs/Biology/FormativeAssessment+/Bio_C3_LO2.png)
|
40
|
+
def process_stem(val)
|
41
|
+
val.gsub(/\(image: ([^\)]*)\)/, %{<img src="\\1" />})
|
42
|
+
end
|
43
|
+
|
44
|
+
def process_distractors(row)
|
45
|
+
[row[DISTRACTOR_1], row[DISTRACTOR_2]]
|
46
|
+
end
|
47
|
+
|
48
|
+
def generate_cc_question
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def add_to_assessment(assessment)
|
53
|
+
if q = generate_cc_question
|
54
|
+
assessment.items << q
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def add_to_group(assessment)
|
59
|
+
if q = generate_cc_question
|
60
|
+
assessment.questions << q
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def to_qti
|
65
|
+
''
|
66
|
+
end
|
67
|
+
|
68
|
+
def type
|
69
|
+
"unknown"
|
70
|
+
end
|
71
|
+
|
72
|
+
def to_s
|
73
|
+
type
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
require 'csv2qti/question_types/multiple_choice'
|
80
|
+
require 'csv2qti/question_types/multiple_select'
|
81
|
+
require 'csv2qti/question_types/matching'
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Csv2qti
|
2
|
+
class Matching < Question
|
3
|
+
|
4
|
+
attr_reader :distractors, :answers
|
5
|
+
|
6
|
+
def initialize(row)
|
7
|
+
super(row)
|
8
|
+
@answers = process_answers(row[CORRECT])
|
9
|
+
end
|
10
|
+
|
11
|
+
# parse out the matching answers that look like this:
|
12
|
+
# "A—Alpha Carbon\nB—Amino Group\nC—Carboxyl Group\nD—Hydrogen Atom\nE—R Group"
|
13
|
+
def process_answers(val)
|
14
|
+
val.split("\n").map{|m|m.split("—")}
|
15
|
+
end
|
16
|
+
|
17
|
+
def type
|
18
|
+
'MATCHING'
|
19
|
+
end
|
20
|
+
|
21
|
+
def generate_cc_question
|
22
|
+
question = CanvasCc::CanvasCC::Models::Question.create('matching_question')
|
23
|
+
question.identifier = rand(10000)
|
24
|
+
question.material = stem
|
25
|
+
#question.distractors = %w(distractor1 distractor2)
|
26
|
+
question.matches = @answers.map {|a| make_answer(a)}
|
27
|
+
|
28
|
+
question
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_qti
|
32
|
+
Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |node|
|
33
|
+
CanvasCc::CanvasCC::MatchingQuestionWriter.write_question(node, generate_cc_question)
|
34
|
+
end.doc.to_xml
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def make_answer(match)
|
40
|
+
{:id => rand(10000).to_s,
|
41
|
+
:question_text => match.first,
|
42
|
+
:question_text_format => '1',
|
43
|
+
:answer_text => match.last}
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Csv2qti
|
2
|
+
class MultipleChoice < Question
|
3
|
+
|
4
|
+
attr_reader :distractors, :answer
|
5
|
+
|
6
|
+
def initialize(row)
|
7
|
+
super(row)
|
8
|
+
@distractors = process_distractors(row)
|
9
|
+
@answer = row[CORRECT]
|
10
|
+
end
|
11
|
+
|
12
|
+
def type
|
13
|
+
'MULTIPLE_CHOICE'
|
14
|
+
end
|
15
|
+
|
16
|
+
def generate_cc_question
|
17
|
+
question = CanvasCc::CanvasCC::Models::Question.create('multiple_choice_question')
|
18
|
+
question.material = stem
|
19
|
+
question.identifier = rand(10000)
|
20
|
+
question.answers = @distractors.map {|d| make_answer(d)} << make_answer(@answer, true)
|
21
|
+
|
22
|
+
question
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_qti
|
26
|
+
Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |node|
|
27
|
+
CanvasCc::CanvasCC::MultipleChoiceQuestionWriter.write_question(node, generate_cc_question)
|
28
|
+
end.doc.to_xml
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def make_answer(text, correct=false)
|
34
|
+
answer = CanvasCc::CanvasCC::Models::Answer.new
|
35
|
+
answer.id = rand(10000)
|
36
|
+
answer.fraction = correct ? 1.0 : 0.0
|
37
|
+
answer.answer_text = text
|
38
|
+
answer
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Csv2qti
|
2
|
+
class MultipleSelect < Question
|
3
|
+
|
4
|
+
attr_reader :distractors, :answers
|
5
|
+
|
6
|
+
def initialize(row)
|
7
|
+
super(row)
|
8
|
+
@distractors = process_distractors(row)
|
9
|
+
@answers = process_answers(row[CORRECT])
|
10
|
+
end
|
11
|
+
|
12
|
+
# parse out the multiple answers that look like this:
|
13
|
+
# "*Helper\n*chains\n*Lower energies"
|
14
|
+
def process_answers(val)
|
15
|
+
val.split("\n").map { |a| a.gsub(/\A\*/, '') }
|
16
|
+
end
|
17
|
+
|
18
|
+
def type
|
19
|
+
'MULTIPLE_SELECT'
|
20
|
+
end
|
21
|
+
|
22
|
+
def generate_cc_question
|
23
|
+
question = CanvasCc::CanvasCC::Models::Question.create('multiple_answers_question')
|
24
|
+
question.material = stem
|
25
|
+
question.identifier = rand(10000)
|
26
|
+
question.answers = @answers.map { |a| make_answer(a, true) } + @distractors.map { |d| make_answer(d) }
|
27
|
+
question
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_qti
|
31
|
+
Nokogiri::XML::Builder.new(encoding: 'UTF-8') do |node|
|
32
|
+
CanvasCc::CanvasCC::MultipleAnswersQuestionWriter.write_question(node, generate_cc_question)
|
33
|
+
end.doc.to_xml
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def make_answer(text, correct=false)
|
39
|
+
answer = CanvasCc::CanvasCC::Models::Answer.new
|
40
|
+
answer.id = rand(10000)
|
41
|
+
answer.fraction = correct ? 1.0 : 0.0
|
42
|
+
answer.answer_text = text
|
43
|
+
answer
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
data/lib/csv2qti.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require "csv2qti/version"
|
2
|
+
require 'csv'
|
3
|
+
require 'pp'
|
4
|
+
require 'canvas_cc'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
module Csv2qti
|
8
|
+
|
9
|
+
class Csv2qtiError < StandardError
|
10
|
+
end
|
11
|
+
|
12
|
+
class QuestionParseError < Csv2qtiError
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.process_dir(path='.')
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.process_file(path)
|
20
|
+
course = nil
|
21
|
+
current_primary = nil
|
22
|
+
current_enabling = nil
|
23
|
+
|
24
|
+
CSV.foreach(path, skip_blanks: true) do |row|
|
25
|
+
begin
|
26
|
+
if $INPUT_LINE_NUMBER == 1
|
27
|
+
raise Csv2qtiError, "Course title not in first cell" unless row[0]
|
28
|
+
course = Course.new(row[0], path)
|
29
|
+
next
|
30
|
+
end
|
31
|
+
|
32
|
+
if row[1] == 'Title'
|
33
|
+
# new Primary outcome
|
34
|
+
current_primary = nil
|
35
|
+
current_enabling = nil
|
36
|
+
elsif row[0]
|
37
|
+
# new outcome row
|
38
|
+
current_enabling = nil
|
39
|
+
if current_primary
|
40
|
+
# New enabling outcome
|
41
|
+
current_enabling = current_primary.add_enabling_outcome(row)
|
42
|
+
# there is usually a question there too
|
43
|
+
current_enabling.add_question(row)
|
44
|
+
else
|
45
|
+
# New primary Outcome
|
46
|
+
current_primary = course.add_primary_outcome(row)
|
47
|
+
end
|
48
|
+
elsif current_enabling
|
49
|
+
current_enabling.add_question(row)
|
50
|
+
elsif row[2] == "Enabling Learning Outcome"
|
51
|
+
# Header row for the enabling outcomes
|
52
|
+
# If the CSVs aren't consistent we may need to use this
|
53
|
+
else
|
54
|
+
puts "what is row #{$INPUT_LINE_NUMBER}?"
|
55
|
+
puts row
|
56
|
+
end
|
57
|
+
rescue QuestionParseError => e
|
58
|
+
puts "line #{$INPUT_LINE_NUMBER + 1}: Problem parsing question: #{$!.message} (just Skipping)"
|
59
|
+
rescue Csv2qtiError => e
|
60
|
+
puts "line #{$INPUT_LINE_NUMBER + 1}: Error: #{$!.message}"
|
61
|
+
raise e
|
62
|
+
rescue
|
63
|
+
puts "line #{$INPUT_LINE_NUMBER + 1}: Unknown Error for this data:"
|
64
|
+
puts row
|
65
|
+
raise $!
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
course
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
require 'csv2qti/course'
|
74
|
+
require 'csv2qti/outcome'
|
75
|
+
require 'csv2qti/question'
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module QuestionFixtures
|
2
|
+
MC = [nil, nil, nil, nil, "OLI: Intro to Biology, module 30, p182-183", "CC BY-NC-SA ", nil,
|
3
|
+
"mc", "Human eye color is a ____ trait.", "polygenic", "epistatic", "epigenetic.", "CC", nil]
|
4
|
+
|
5
|
+
IMAGE = ["8.2", "Genetics and Disease", "Explain the conventions...", "a0db89e6-9afe-4864-ac7b-29843833595e", "Genetics and Disease Outline", "CC BY", "4/10/2015",
|
6
|
+
"mc", "Given the punnett square below, choose the right answer? (image: https://example.com/Bio_C8_LO2.jpg)", "Half", "All will have red eyes", "All will have white eyes.", nil, nil]
|
7
|
+
|
8
|
+
MS = [nil, nil, nil, nil, nil, nil, nil,
|
9
|
+
"mmc", "Select the answers that help define enzymes.", "*Helper\n*chains\n*Lower energies",
|
10
|
+
"force a reaction", "Increase energy", nil, "edited"]
|
11
|
+
|
12
|
+
MATCHING = [nil, nil, nil, nil, "Proteins (Part I)", "CC BY", "4/14/2015",
|
13
|
+
"matching", "Match the letters to the molecular components.",
|
14
|
+
"A—Alpha Carbon\nB—Amino Group\nC—Carboxyl Group\nD—Hydrogen Atom\nE—R Group", nil, nil, nil, nil]
|
15
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'minitest/spec'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'csv2qti'
|
4
|
+
require_relative 'fixtures/quiz_questions'
|
5
|
+
|
6
|
+
describe Csv2qti::Matching do
|
7
|
+
|
8
|
+
subject { Csv2qti::Matching.new(QuestionFixtures::MATCHING) }
|
9
|
+
|
10
|
+
it "pulls out the matches" do
|
11
|
+
subject.answers.must_equal [["A", "Alpha Carbon"],
|
12
|
+
["B", "Amino Group"],
|
13
|
+
["C", "Carboxyl Group"],
|
14
|
+
["D", "Hydrogen Atom"],
|
15
|
+
["E", "R Group"]]
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'minitest/spec'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'csv2qti'
|
4
|
+
require_relative 'fixtures/quiz_questions'
|
5
|
+
|
6
|
+
describe Csv2qti::MultipleChoice do
|
7
|
+
|
8
|
+
subject { Csv2qti::MultipleChoice.new(QuestionFixtures::MC) }
|
9
|
+
|
10
|
+
it "pulls out the stem" do
|
11
|
+
subject.stem.must_equal "Human eye color is a ____ trait."
|
12
|
+
end
|
13
|
+
|
14
|
+
it "pulls out the answer" do
|
15
|
+
subject.answer.must_equal "polygenic"
|
16
|
+
end
|
17
|
+
|
18
|
+
it "pulls out the distractors" do
|
19
|
+
subject.distractors.must_equal ["epistatic", "epigenetic."]
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'minitest/spec'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'csv2qti'
|
4
|
+
require_relative 'fixtures/quiz_questions'
|
5
|
+
|
6
|
+
describe Csv2qti::MultipleSelect do
|
7
|
+
|
8
|
+
subject { Csv2qti::MultipleSelect.new(QuestionFixtures::MS) }
|
9
|
+
|
10
|
+
it "pulls out the answers" do
|
11
|
+
subject.answers.must_equal ['Helper', 'chains', 'Lower energies']
|
12
|
+
end
|
13
|
+
|
14
|
+
it "pulls out the distractors" do
|
15
|
+
subject.distractors.must_equal ["force a reaction", "Increase energy"]
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'minitest/spec'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'csv2qti'
|
4
|
+
require_relative 'fixtures/quiz_questions'
|
5
|
+
|
6
|
+
describe Csv2qti::Outcome do
|
7
|
+
|
8
|
+
subject { Csv2qti::Outcome.new(QuestionFixtures::IMAGE) }
|
9
|
+
|
10
|
+
it "pulls out number" do
|
11
|
+
subject.number.must_equal '8.2'
|
12
|
+
end
|
13
|
+
|
14
|
+
it "pulls out the title" do
|
15
|
+
subject.title.must_equal "Genetics and Disease"
|
16
|
+
end
|
17
|
+
|
18
|
+
it "pulls out the description" do
|
19
|
+
subject.description.must_equal "Explain the conventions..."
|
20
|
+
end
|
21
|
+
|
22
|
+
it "pulls out the license" do
|
23
|
+
subject.license.must_equal "CC BY"
|
24
|
+
end
|
25
|
+
|
26
|
+
it "pulls out the GUID" do
|
27
|
+
subject.guid.must_equal "a0db89e6-9afe-4864-ac7b-29843833595e"
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'minitest/spec'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
require 'csv2qti'
|
4
|
+
require_relative 'fixtures/quiz_questions'
|
5
|
+
|
6
|
+
describe Csv2qti::MultipleChoice do
|
7
|
+
|
8
|
+
subject { Csv2qti::Question.new(QuestionFixtures::MC) }
|
9
|
+
|
10
|
+
it "pulls out the stem" do
|
11
|
+
subject.stem.must_equal "Human eye color is a ____ trait."
|
12
|
+
end
|
13
|
+
|
14
|
+
it "pulls out the license" do
|
15
|
+
subject.license.must_equal "CC"
|
16
|
+
end
|
17
|
+
|
18
|
+
it "converts image text" do
|
19
|
+
q = Csv2qti::Question.new(QuestionFixtures::IMAGE)
|
20
|
+
q.stem.must_equal %{Given the punnett square below, choose the right answer? <img src="https://example.com/Bio_C8_LO2.jpg" />}
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: csv2qti_lumen
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Bracken Mosbacker
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-06-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: canvas_cc
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.7'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.7'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '10.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '10.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description:
|
70
|
+
email:
|
71
|
+
- bracken@lumenlearning.com
|
72
|
+
executables:
|
73
|
+
- csv2qti
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- ".gitignore"
|
78
|
+
- Gemfile
|
79
|
+
- LICENSE.txt
|
80
|
+
- README.md
|
81
|
+
- Rakefile
|
82
|
+
- bin/csv2qti
|
83
|
+
- csv2qti.gemspec
|
84
|
+
- lib/csv2qti.rb
|
85
|
+
- lib/csv2qti/course.rb
|
86
|
+
- lib/csv2qti/outcome.rb
|
87
|
+
- lib/csv2qti/question.rb
|
88
|
+
- lib/csv2qti/question_types/matching.rb
|
89
|
+
- lib/csv2qti/question_types/multiple_choice.rb
|
90
|
+
- lib/csv2qti/question_types/multiple_select.rb
|
91
|
+
- lib/csv2qti/version.rb
|
92
|
+
- test/fixtures/quiz_questions.rb
|
93
|
+
- test/matching_test.rb
|
94
|
+
- test/multiple_choice_test.rb
|
95
|
+
- test/multiple_select_test.rb
|
96
|
+
- test/outcome_test.rb
|
97
|
+
- test/question_test.rb
|
98
|
+
homepage: ''
|
99
|
+
licenses:
|
100
|
+
- MIT
|
101
|
+
metadata: {}
|
102
|
+
post_install_message:
|
103
|
+
rdoc_options: []
|
104
|
+
require_paths:
|
105
|
+
- lib
|
106
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
version: '0'
|
116
|
+
requirements: []
|
117
|
+
rubyforge_project:
|
118
|
+
rubygems_version: 2.4.6
|
119
|
+
signing_key:
|
120
|
+
specification_version: 4
|
121
|
+
summary: Converts a specifically-formatted CSV into QTI.
|
122
|
+
test_files:
|
123
|
+
- test/fixtures/quiz_questions.rb
|
124
|
+
- test/matching_test.rb
|
125
|
+
- test/multiple_choice_test.rb
|
126
|
+
- test/multiple_select_test.rb
|
127
|
+
- test/outcome_test.rb
|
128
|
+
- test/question_test.rb
|