ruql 0.0.9 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
|