ruql 0.0.9 → 0.1.3
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 +4 -4
- data/bin/ruql +3 -7
- data/lib/ruql/answer.rb +14 -2
- data/lib/ruql/multiple_choice.rb +1 -1
- data/lib/ruql/open_assessment/open_assessment.rb +1 -1
- data/lib/ruql/question.rb +46 -4
- data/lib/ruql/quiz.rb +7 -10
- data/lib/ruql/renderers/edxml_renderer.rb +1 -1
- data/lib/ruql/renderers/html5_renderer.rb +13 -19
- data/lib/ruql/renderers/json_renderer.rb +1 -35
- data/lib/ruql/select_multiple.rb +0 -1
- data/lib/ruql/true_false.rb +1 -1
- data/lib/ruql.rb +1 -7
- metadata +50 -9
- data/lib/ruql/renderers/xml_renderer.rb +0 -148
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8a6637987324f5db32e7387b7183ea4591957817
|
4
|
+
data.tar.gz: 05dc9bbffafb43de71688bbbd1a0b70302d51665
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bd17352a49b61a9b0844cb0d043093c3ba8fe33ba302ac83aac60cb602a4f534074f7c02c4dd0f0772d301232dd360c57d997ddd6fd67ac171172983b0e8c93b
|
7
|
+
data.tar.gz: 2f34ee1cd6b63f9f81ccd6bafecc7864143d4512d127d1a5740e035f73d9cf5c83abd68bd0eda6b232837bb1a68e60223977ca38920c9a8926b1d167779fd25d
|
data/bin/ruql
CHANGED
@@ -15,7 +15,6 @@ renderer choices are:
|
|
15
15
|
HtmlForm - HTML forms
|
16
16
|
EdXml - XML for OpenEdX platform in-course questions
|
17
17
|
AutoQCM - LaTeX for use with AutoQCM (http://home.gna.org/auto-qcm)
|
18
|
-
Coursera - [obsolete] Coursera HTML/XML format for online auto-grading
|
19
18
|
JSON - [partially implemented] JSON format
|
20
19
|
Qualtrics -[partially implemented] Qualtrics survey (txt) format
|
21
20
|
|
@@ -39,7 +38,7 @@ The EdXML renderer supports these options:
|
|
39
38
|
some systems.
|
40
39
|
|
41
40
|
-y <file.yml>, --yaml=<file.yml>
|
42
|
-
Render
|
41
|
+
Render open-assessment questions using info in given Yaml file.
|
43
42
|
|
44
43
|
The HTML5 and HTML Forms renderers supports these options:
|
45
44
|
-o <type>, --list-type=<type>
|
@@ -47,14 +46,13 @@ The HTML5 and HTML Forms renderers supports these options:
|
|
47
46
|
Sets the values of the 'start' and 'type' attributes for the HTML <ol>
|
48
47
|
element. Type should be one of 1,a,A,i,I. Start should always be
|
49
48
|
a number indicating the ordinal value (starting from 1) of first item.
|
50
|
-
-c <href>, --css=<href>
|
51
|
-
embed <href> for stylesheet into generated HTML5
|
52
49
|
-j <src>, --js=<src>
|
53
50
|
embed <src> for JavaScript
|
54
51
|
-t <file.html.erb>, --template=<file.html.erb>
|
55
52
|
Use file.html.erb as HTML template rather than generating our own file.
|
56
53
|
file.html.erb should have <%= yield %> where questions should go.
|
57
|
-
|
54
|
+
The default template in the templates/ directory of RuQL will be used
|
55
|
+
if this option is not given.
|
58
56
|
The following local variables will be replaced with their values in
|
59
57
|
the template:
|
60
58
|
<%= quiz.title %> - the quiz title
|
@@ -99,11 +97,9 @@ def main
|
|
99
97
|
['-l', '--log', Getopt::REQUIRED],
|
100
98
|
['-y', '--yaml', Getopt::REQUIRED]
|
101
99
|
)
|
102
|
-
Quiz.set_yaml_file opts.delete("y") || opts.delete("yaml")
|
103
100
|
Quiz.instance_eval "#{IO.read(filename)}"
|
104
101
|
Quiz.quizzes.each { |quiz| puts quiz.render_with(renderer, opts) }
|
105
102
|
end
|
106
103
|
|
107
104
|
usage if ARGV.length < 2
|
108
105
|
main
|
109
|
-
|
data/lib/ruql/answer.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
class Answer
|
2
2
|
include Comparable
|
3
|
-
attr_accessor :answer_text, :explanation
|
3
|
+
attr_accessor :answer_text, :explanation, :correct
|
4
4
|
attr_reader :correct
|
5
5
|
attr_reader :builder
|
6
6
|
attr_reader :question
|
@@ -8,9 +8,21 @@ class Answer
|
|
8
8
|
def <=>(other) ; self.answer_text <=> other.answer_text ; end
|
9
9
|
def correct? ; !!correct ; end
|
10
10
|
def has_explanation? ; !!explanation ; end
|
11
|
-
def initialize(answer_text, correct, explanation=nil)
|
11
|
+
def initialize(answer_text = '', correct = false, explanation=nil)
|
12
12
|
@answer_text = answer_text
|
13
13
|
@correct = !!correct # ensure boolean
|
14
14
|
@explanation = explanation
|
15
15
|
end
|
16
|
+
|
17
|
+
def to_JSON
|
18
|
+
Hash[instance_variables.collect { |var| [var.to_s.delete('@'), instance_variable_get(var)] }]
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.from_JSON(hash)
|
22
|
+
answer = Answer.new
|
23
|
+
hash.each do |key, value|
|
24
|
+
answer.send((key + '=').to_sym, value)
|
25
|
+
end
|
26
|
+
answer
|
27
|
+
end
|
16
28
|
end
|
data/lib/ruql/multiple_choice.rb
CHANGED
@@ -115,7 +115,7 @@ class OpenAssessment
|
|
115
115
|
# Adds fields for a simple_open_assessment question
|
116
116
|
def add_simple_question
|
117
117
|
criterion = Criterion.new
|
118
|
-
criterion.name("How'd you do?")
|
118
|
+
criterion.name("How'd you do?") # "
|
119
119
|
criterion.label("Scoring Rubric")
|
120
120
|
|
121
121
|
raise "Must have answer for question" if @question_answer.nil?
|
data/lib/ruql/question.rb
CHANGED
@@ -1,24 +1,40 @@
|
|
1
1
|
class Question
|
2
|
-
attr_accessor :question_text,
|
3
|
-
|
2
|
+
attr_accessor :question_text,
|
3
|
+
:answers,
|
4
|
+
:question_image,
|
5
|
+
:randomize,
|
6
|
+
:points,
|
7
|
+
:name,
|
8
|
+
:question_tags,
|
9
|
+
:question_uid,
|
10
|
+
:question_comment,
|
11
|
+
:raw
|
12
|
+
|
4
13
|
def initialize(*args)
|
5
14
|
options = if args[-1].kind_of?(Hash) then args[-1] else {} end
|
6
15
|
@answers = options[:answers] || []
|
7
16
|
@points = [options[:points].to_i, 1].max
|
8
17
|
@raw = options[:raw]
|
9
18
|
@name = options[:name]
|
19
|
+
@question_image = options[:image]
|
10
20
|
@question_tags = []
|
21
|
+
@question_uid = (options.delete(:uid) || SecureRandom.uuid).to_s
|
11
22
|
@question_comment = ''
|
12
23
|
end
|
13
|
-
|
14
24
|
def raw? ; !!@raw ; end
|
15
|
-
|
25
|
+
|
26
|
+
def uid(u) ; @question_uid = u ; end
|
27
|
+
|
16
28
|
def text(s) ; @question_text = s ; end
|
17
29
|
|
18
30
|
def explanation(text)
|
19
31
|
@answers.each { |answer| answer.explanation ||= text }
|
20
32
|
end
|
21
33
|
|
34
|
+
def image(url)
|
35
|
+
@question_image = url
|
36
|
+
end
|
37
|
+
|
22
38
|
def answer(text, opts={})
|
23
39
|
@answers << Answer.new(text, correct=true, opts[:explanation])
|
24
40
|
end
|
@@ -44,4 +60,30 @@ class Question
|
|
44
60
|
|
45
61
|
def correct_answers ; @answers.collect(&:correct?) ; end
|
46
62
|
|
63
|
+
def answer_helper(obj)
|
64
|
+
if obj.is_a? Array and obj.size and obj[0].is_a? Answer
|
65
|
+
return obj.map {|answer| answer.to_JSON}
|
66
|
+
end
|
67
|
+
obj
|
68
|
+
end
|
69
|
+
|
70
|
+
#creates a JSON hash of the object with its object name. we should convert this to a mixin for answer and question. aaron
|
71
|
+
def to_JSON
|
72
|
+
h = Hash[instance_variables.collect { |var| [var.to_s.delete('@'), answer_helper(instance_variable_get(var))] }]
|
73
|
+
h['question_type'] = self.class.to_s
|
74
|
+
return h
|
75
|
+
end
|
76
|
+
|
77
|
+
#factory method to return correct type of question
|
78
|
+
def self.from_JSON(hash_str)
|
79
|
+
hash = JSON.parse(hash_str)
|
80
|
+
#create the appropriate class of the object from the hash's class name
|
81
|
+
question = Object.const_get(hash.fetch('question_type')).new()
|
82
|
+
hash.reject{|key| key == 'answers' or key == 'question_type'}.each do |key, value|
|
83
|
+
question.send((key + '=').to_sym, value)
|
84
|
+
end
|
85
|
+
question.answers = hash['answers'].map{|answer_hash| Answer.from_JSON(answer_hash)}
|
86
|
+
question
|
87
|
+
end
|
88
|
+
|
47
89
|
end
|
data/lib/ruql/quiz.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
class Quiz
|
2
2
|
@@quizzes = []
|
3
|
-
@@yaml_file = nil
|
4
3
|
@quiz_yaml = {}
|
5
4
|
def self.quizzes ; @@quizzes ; end
|
6
5
|
@@default_options =
|
@@ -27,18 +26,20 @@ class Quiz
|
|
27
26
|
attr_reader :output
|
28
27
|
attr_reader :seed
|
29
28
|
attr_reader :logger
|
30
|
-
attr_accessor :title
|
29
|
+
attr_accessor :title, :quizzes
|
31
30
|
|
32
|
-
def initialize(title,
|
31
|
+
def initialize(title, options={})
|
33
32
|
@output = ''
|
34
|
-
@questions = options
|
33
|
+
@questions = options.delete(:questions) || []
|
35
34
|
@title = title
|
36
35
|
@options = @@default_options.merge(options)
|
37
36
|
@seed = srand
|
38
37
|
@logger = Logger.new(STDERR)
|
39
38
|
@logger.level = Logger.const_get (options.delete('l') ||
|
40
39
|
options.delete('log') || 'warn').upcase
|
41
|
-
|
40
|
+
if (yaml = options.delete(:yaml))
|
41
|
+
@quiz_yaml = YAML::load(IO.read yaml)
|
42
|
+
end
|
42
43
|
end
|
43
44
|
|
44
45
|
def self.get_renderer(renderer)
|
@@ -52,10 +53,6 @@ class Quiz
|
|
52
53
|
@output = @renderer.output
|
53
54
|
end
|
54
55
|
|
55
|
-
def self.set_yaml_file(file)
|
56
|
-
@@yaml_file = file && YAML::load_file(file)
|
57
|
-
end
|
58
|
-
|
59
56
|
def points ; questions.map(&:points).inject { |sum,points| sum + points } ; end
|
60
57
|
|
61
58
|
def num_questions ; questions.length ; end
|
@@ -130,7 +127,7 @@ class Quiz
|
|
130
127
|
end
|
131
128
|
|
132
129
|
def self.quiz(*args, &block)
|
133
|
-
quiz = Quiz.new(*args
|
130
|
+
quiz = Quiz.new(*args)
|
134
131
|
quiz.instance_eval(&block)
|
135
132
|
@@quizzes << quiz
|
136
133
|
end
|
@@ -5,11 +5,10 @@ class Html5Renderer
|
|
5
5
|
attr_reader :output
|
6
6
|
|
7
7
|
def initialize(quiz,options={})
|
8
|
-
@css = options.delete('c') || options.delete('css')
|
9
8
|
@show_solutions = options.delete('s') || options.delete('solutions')
|
10
9
|
@template = options.delete('t') ||
|
11
10
|
options.delete('template') ||
|
12
|
-
File.join(
|
11
|
+
File.join(File.dirname(__FILE__), '../../../templates/html5.html.erb')
|
13
12
|
@output = ''
|
14
13
|
@list_type = options.delete('o') || options.delete('list-type') || '1'
|
15
14
|
@list_start = options.delete('a') || options.delete('list-start') || '1'
|
@@ -18,21 +17,9 @@ class Html5Renderer
|
|
18
17
|
end
|
19
18
|
|
20
19
|
def render_quiz
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
@output
|
25
|
-
end
|
26
|
-
else
|
27
|
-
@h.html do
|
28
|
-
@h.head do
|
29
|
-
@h.title @quiz.title
|
30
|
-
@h.link(:rel => 'stylesheet', :type =>'text/css', :href=>@css) if @css
|
31
|
-
end
|
32
|
-
@h.body do
|
33
|
-
render_questions
|
34
|
-
end
|
35
|
-
end
|
20
|
+
render_with_template do
|
21
|
+
render_questions
|
22
|
+
@output
|
36
23
|
end
|
37
24
|
self
|
38
25
|
end
|
@@ -124,10 +111,17 @@ class Html5Renderer
|
|
124
111
|
def render_question_text(question,index)
|
125
112
|
html_args = {
|
126
113
|
:id => "question-#{index}",
|
127
|
-
:
|
128
|
-
|
114
|
+
:'data-uid' => question.question_uid,
|
115
|
+
:class => ['question', question.class.to_s.downcase, (question.multiple ? 'multiple' : '')].join(' ')
|
129
116
|
}
|
117
|
+
if question.question_image # add CSS class to both <li> and <img>
|
118
|
+
html_args[:class] << 'question-with-image'
|
119
|
+
end
|
130
120
|
@h.li html_args do
|
121
|
+
# if there's an image, render it first
|
122
|
+
if question.question_image
|
123
|
+
@h.img :src => question.question_image, :class => 'question-image'
|
124
|
+
end
|
131
125
|
@h.div :class => 'text' do
|
132
126
|
qtext = "[#{question.points} point#{'s' if question.points>1}] " <<
|
133
127
|
('Select ALL that apply: ' if question.multiple).to_s <<
|
@@ -10,40 +10,6 @@ class JSONRenderer
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def render_quiz
|
13
|
-
@quiz.questions.
|
14
|
-
case question
|
15
|
-
when MultipleChoice, SelectMultiple, TrueFalse then render_multiple_choice(question)
|
16
|
-
when FillIn then render_fill_in(question) # not currently supported
|
17
|
-
else
|
18
|
-
raise "Unknown question type: #{question}"
|
19
|
-
end
|
20
|
-
end
|
21
|
-
@output = JSON.pretty_generate(@json_array)
|
13
|
+
@output = @quiz.questions.map {|question| JSON.pretty_generate(question.to_JSON)}
|
22
14
|
end
|
23
|
-
|
24
|
-
def render_multiple_choice(question)
|
25
|
-
question_hash = {
|
26
|
-
"text" => question.question_text,
|
27
|
-
"answers" => answers_to_json_array(question.answers)
|
28
|
-
}
|
29
|
-
@json_array.push(question_hash)
|
30
|
-
end
|
31
|
-
|
32
|
-
def answers_to_json_array(answers)
|
33
|
-
answers_array = []
|
34
|
-
answers.each do |answer|
|
35
|
-
answer_json = {
|
36
|
-
"text" => answer.answer_text,
|
37
|
-
"explanation" => answer.explanation,
|
38
|
-
"correct" => answer.correct
|
39
|
-
}
|
40
|
-
answers_array.push(answer_json)
|
41
|
-
end
|
42
|
-
answers_array
|
43
|
-
end
|
44
|
-
|
45
|
-
def render_fill_in(q)
|
46
|
-
# fill-in-the-blank questions not currently supported
|
47
|
-
end
|
48
|
-
|
49
15
|
end
|
data/lib/ruql/select_multiple.rb
CHANGED
data/lib/ruql/true_false.rb
CHANGED
data/lib/ruql.rb
CHANGED
@@ -6,13 +6,7 @@ require 'date'
|
|
6
6
|
$LOAD_PATH.unshift File.expand_path(File.join(File.dirname(__FILE__)))
|
7
7
|
|
8
8
|
# renderers
|
9
|
-
|
10
|
-
require 'ruql/renderers/html5_renderer'
|
11
|
-
require 'ruql/renderers/html_form_renderer'
|
12
|
-
require 'ruql/renderers/edxml_renderer'
|
13
|
-
require 'ruql/renderers/auto_qcm_renderer'
|
14
|
-
require 'ruql/renderers/json_renderer'
|
15
|
-
require 'ruql/renderers/qualtrics_renderer'
|
9
|
+
Dir[File.join(File.dirname(__FILE__),'ruql/renderers/*.rb')].each { |file| require file }
|
16
10
|
|
17
11
|
# question types
|
18
12
|
require 'ruql/quiz'
|
metadata
CHANGED
@@ -1,43 +1,85 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Armando Fox
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-11-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
15
15
|
requirements:
|
16
|
-
- -
|
16
|
+
- - '>='
|
17
17
|
- !ruby/object:Gem::Version
|
18
18
|
version: '3.0'
|
19
19
|
prerelease: false
|
20
20
|
name: builder
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
22
22
|
requirements:
|
23
|
-
- -
|
23
|
+
- - '>='
|
24
24
|
- !ruby/object:Gem::Version
|
25
25
|
version: '3.0'
|
26
26
|
type: :runtime
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
29
29
|
requirements:
|
30
|
-
- - '
|
30
|
+
- - '>='
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version: 1.
|
32
|
+
version: '1.0'
|
33
33
|
prerelease: false
|
34
34
|
name: getopt
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
|
-
- - '
|
37
|
+
- - '>='
|
38
38
|
- !ruby/object:Gem::Version
|
39
|
-
version: 1.
|
39
|
+
version: '1.0'
|
40
40
|
type: :runtime
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '2.0'
|
47
|
+
prerelease: false
|
48
|
+
name: rspec
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '2.0'
|
54
|
+
type: :development
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ~>
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '4.0'
|
61
|
+
prerelease: false
|
62
|
+
name: activesupport
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ~>
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '4.0'
|
68
|
+
type: :development
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - '>='
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
prerelease: false
|
76
|
+
name: byebug
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - '>='
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
type: :development
|
41
83
|
description: Ruby-embedded DSL for creating short-answer quiz questions
|
42
84
|
email: fox@cs.berkeley.edu
|
43
85
|
executables:
|
@@ -65,7 +107,6 @@ files:
|
|
65
107
|
- lib/ruql/renderers/html_form_renderer.rb
|
66
108
|
- lib/ruql/renderers/json_renderer.rb
|
67
109
|
- lib/ruql/renderers/qualtrics_renderer.rb
|
68
|
-
- lib/ruql/renderers/xml_renderer.rb
|
69
110
|
- lib/ruql/select_multiple.rb
|
70
111
|
- lib/ruql/tex_output.rb
|
71
112
|
- lib/ruql/true_false.rb
|
@@ -1,148 +0,0 @@
|
|
1
|
-
class XMLRenderer
|
2
|
-
require 'builder'
|
3
|
-
|
4
|
-
attr_reader :output
|
5
|
-
def initialize(quiz,options={})
|
6
|
-
@output = ''
|
7
|
-
@b = Builder::XmlMarkup.new(:target => @output, :indent => 2)
|
8
|
-
@quiz = quiz
|
9
|
-
end
|
10
|
-
|
11
|
-
def render(thing)
|
12
|
-
case thing
|
13
|
-
when MultipleChoice,SelectMultiple,TrueFalse then render_multiple_choice(thing)
|
14
|
-
when FillIn then render_fill_in(thing)
|
15
|
-
else
|
16
|
-
raise "Unknown question type: #{thing}"
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def render_quiz
|
21
|
-
# entire quiz can be in one question group, as long as we specify
|
22
|
-
# that ALL question from the group must be used to make the quiz.
|
23
|
-
xml_quiz do
|
24
|
-
# after preamble...
|
25
|
-
@b.question_groups do
|
26
|
-
@b.question_group(:select => @quiz.questions.length) do
|
27
|
-
@quiz.questions.each do |question|
|
28
|
-
self.render(question)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
@output
|
34
|
-
end
|
35
|
-
|
36
|
-
def render_fill_in(question)
|
37
|
-
@b.question :type => 'GS_Short_Answer_Question_Simple', :id => question.object_id.to_s(16) do
|
38
|
-
@b.metadata {
|
39
|
-
@b.parameters {
|
40
|
-
@b.rescale_score question.points
|
41
|
-
@b.type 'regexp'
|
42
|
-
}
|
43
|
-
}
|
44
|
-
# since we want all the options to appear, we create N option
|
45
|
-
# groups each containig 1 option, and specify that option to
|
46
|
-
# always be selected for inclusion in the quiz. If the original
|
47
|
-
# question specified 'random', use the 'randomize' attribute on
|
48
|
-
# option_groups to scramble the order in which displayed;
|
49
|
-
# otherwise, display in same order as answers appear in source.
|
50
|
-
@b.data {
|
51
|
-
@b.text { @b.cdata!(question.question_text) }
|
52
|
-
@b.option_groups(:randomize => !!question.randomize) {
|
53
|
-
@b.option_group(:select => 'all') {
|
54
|
-
question.answers.each do |answer|
|
55
|
-
option_args = {}
|
56
|
-
option_args['selected_score'] = answer.correct? ? 1 : 0
|
57
|
-
option_args['unselected_score'] =
|
58
|
-
question.multiple ? 1 - option_args['selected_score'] : 0
|
59
|
-
option_args['id'] = answer.object_id.to_s(16)
|
60
|
-
@b.option(option_args) do
|
61
|
-
answer_text = answer.answer_text
|
62
|
-
if answer_text.kind_of?(Regexp)
|
63
|
-
answer_text = answer_text.inspect
|
64
|
-
if !question.case_sensitive
|
65
|
-
answer_text += 'i'
|
66
|
-
end
|
67
|
-
end
|
68
|
-
@b.text { @b.cdata!(answer_text) }
|
69
|
-
@b.explanation { @b.cdata!(answer.explanation) } if answer.has_explanation?
|
70
|
-
end
|
71
|
-
end
|
72
|
-
}
|
73
|
-
}
|
74
|
-
}
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
def render_multiple_choice(question)
|
79
|
-
@b.question :type => 'GS_Choice_Answer_Question', :id => question.object_id.to_s(16) do
|
80
|
-
@b.metadata {
|
81
|
-
@b.parameters {
|
82
|
-
@b.rescale_score question.points
|
83
|
-
@b.choice_type (question.multiple ? 'checkbox' : 'radio')
|
84
|
-
}
|
85
|
-
}
|
86
|
-
# since we want all the options to appear, we create N option
|
87
|
-
# groups each containig 1 option, and specify that option to
|
88
|
-
# always be selected for inclusion in the quiz. If the original
|
89
|
-
# question specified 'random', use the 'randomize' attribute on
|
90
|
-
# option_groups to scramble the order in which displayed;
|
91
|
-
# otherwise, display in same order as answers appear in source.
|
92
|
-
@b.data {
|
93
|
-
@b.text { @b.cdata!(question.question_text) }
|
94
|
-
@b.option_groups(:randomize => !!question.randomize) {
|
95
|
-
question.answers.each do |a|
|
96
|
-
@b.option_group(:select => 'all') {
|
97
|
-
self.render_multiple_choice_answer a, question.multiple
|
98
|
-
}
|
99
|
-
end
|
100
|
-
}
|
101
|
-
}
|
102
|
-
end
|
103
|
-
end
|
104
|
-
alias :render_true_false :render_multiple_choice
|
105
|
-
|
106
|
-
def render_multiple_choice_answer(answer, multiple_allowed)
|
107
|
-
option_args = {}
|
108
|
-
option_args['selected_score'] = answer.correct? ? 1 : 0
|
109
|
-
option_args['unselected_score'] =
|
110
|
-
multiple_allowed ? 1 - option_args['selected_score'] : 0
|
111
|
-
option_args['id'] = answer.object_id.to_s(16)
|
112
|
-
@b.option(option_args) do
|
113
|
-
@b.text { @b.cdata!(answer.answer_text) }
|
114
|
-
@b.explanation { @b.cdata!(answer.explanation) } if answer.has_explanation?
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
private
|
119
|
-
|
120
|
-
def options_to_xml(h)
|
121
|
-
h.each_pair do |k,v|
|
122
|
-
if v.is_a?(Hash)
|
123
|
-
@b.__send__(k.to_sym) do
|
124
|
-
options_to_xml v
|
125
|
-
end
|
126
|
-
else
|
127
|
-
@b.__send__(k.to_sym, v)
|
128
|
-
end
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
|
133
|
-
def xml_quiz
|
134
|
-
@b.quiz do
|
135
|
-
@b.metadata do
|
136
|
-
@b.type 'quiz'
|
137
|
-
@b.title @quiz.title
|
138
|
-
options_to_xml @quiz.options
|
139
|
-
end
|
140
|
-
@b.preamble
|
141
|
-
@b.data do
|
142
|
-
yield
|
143
|
-
end
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
end
|
148
|
-
|