ruql 0.0.2 → 0.0.4

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.
data/bin/ruql CHANGED
@@ -4,17 +4,18 @@ require 'ruql'
4
4
  require 'getopt/long'
5
5
 
6
6
  def usage
7
- name = $0.gsub( Regexp.new '^[^/]+/', '')
7
+ name = File.basename $0
8
8
  STDERR.puts <<eos
9
9
  Usage: #{name} filename.rb renderer [options]
10
10
  filename.rb contains questions expressed in RuQL
11
11
 
12
12
  renderer choices are:
13
- Html5 - HTML 5 suitable for Web display/printing
14
- EdXml - XML for OpenEdX platform in-course questions
15
- AutoQCM - LaTeX for use with AutoQCM (http://home.gna.org/auto-qcm)
16
- Coursera- [obsolete] Coursera HTML/XML format for online auto-grading
17
- JSON - [partially implemented] JSON format
13
+ Html5 - HTML 5 suitable for Web display/printing
14
+ HtmlForm - HTML forms
15
+ EdXml - XML for OpenEdX platform in-course questions
16
+ AutoQCM - LaTeX for use with AutoQCM (http://home.gna.org/auto-qcm)
17
+ Coursera - [obsolete] Coursera HTML/XML format for online auto-grading
18
+ JSON - [partially implemented] JSON format
18
19
 
19
20
  Global options:
20
21
  -l <loglevel>, --log=<loglevel>
@@ -35,9 +36,11 @@ The EdXML renderer supports these options:
35
36
  NOTE: The 'points' and 'randomize' attributes of questions are not honored by
36
37
  some systems.
37
38
 
38
- The HTML5 renderer supports these options:
39
+ The HTML5 and HTML Forms renderers supports these options:
39
40
  -c <href>, --css=<href>
40
41
  embed <href> for stylesheet into generated HTML5
42
+ -j <src>, --js=<src>
43
+ embed <src> for JavaScrips
41
44
  -t <file.html.erb>, --template=<file.html.erb>
42
45
  Use file.html.erb as HTML template rather than generating our own file.
43
46
  file.html.erb should have <%= yield %> where questions should go.
@@ -72,6 +75,7 @@ def main
72
75
  raise "Unknown renderer '#{renderer}'" unless Quiz.get_renderer(renderer)
73
76
  opts = Getopt::Long.getopts(
74
77
  ['-c', '--css', Getopt::REQUIRED],
78
+ ['-j', '--js', Getopt::REQUIRED],
75
79
  ['-t', '--template', Getopt::REQUIRED],
76
80
  ['-s', '--solutions', Getopt::BOOLEAN],
77
81
  ['-n', '--name', Getopt::REQUIRED],
data/lib/ruql.rb CHANGED
@@ -7,6 +7,7 @@ $LOAD_PATH.unshift File.expand_path(File.join(File.dirname(__FILE__)))
7
7
  # renderers
8
8
  require 'ruql/renderers/xml_renderer'
9
9
  require 'ruql/renderers/html5_renderer'
10
+ require 'ruql/renderers/html_form_renderer'
10
11
  require 'ruql/renderers/edxml_renderer'
11
12
  require 'ruql/renderers/auto_qcm_renderer'
12
13
  require 'ruql/renderers/json_renderer'
@@ -68,7 +68,7 @@ class Html5Renderer
68
68
  @h.ol :class => 'answers' do
69
69
  answers.each do |answer|
70
70
  if @show_solutions
71
- render_answer_for_solutions(answer, q.raw?)
71
+ render_answer_for_solutions(answer, q.raw?, q.class == TrueFalse)
72
72
  else
73
73
  if q.raw? then @h.li { |l| l << answer.answer_text } else @h.li answer.answer_text end
74
74
  end
@@ -104,8 +104,12 @@ class Html5Renderer
104
104
  end
105
105
  end
106
106
 
107
- def render_answer_for_solutions(answer,raw)
107
+ def render_answer_for_solutions(answer,raw,is_true_false = nil)
108
108
  args = {:class => (answer.correct? ? 'correct' : 'incorrect')}
109
+ if is_true_false
110
+ answer.answer_text.prepend(
111
+ answer.correct? ? "CORRECT: " : "INCORRECT: ")
112
+ end
109
113
  @h.li(args) do
110
114
  if raw then @h.p { |p| p << answer.answer_text } else @h.p answer.answer_text end
111
115
  if answer.has_explanation?
@@ -0,0 +1,198 @@
1
+ class HtmlFormRenderer
2
+ require 'builder'
3
+ require 'erb'
4
+
5
+ attr_reader :output
6
+
7
+ def initialize(quiz,options={})
8
+ @css = options.delete('c') || options.delete('css')
9
+ @js = options.delete('j') || options.delete('js')
10
+ @show_solutions = options.delete('s') || options.delete('solutions')
11
+ @template = options.delete('t') ||
12
+ options.delete('template') #||
13
+ #File.join(Gem.loaded_specs['ruql'].full_gem_path, 'templates/htmlform.html.erb')
14
+ @output = ''
15
+ @quiz = quiz
16
+ @h = Builder::XmlMarkup.new(:target => @output, :indent => 2)
17
+ end
18
+
19
+ def render_quiz
20
+ if @template
21
+ render_with_template do
22
+ render_questions
23
+ @output
24
+ end
25
+ else
26
+ @h.html do
27
+ @h.head do
28
+ @h.title @quiz.title
29
+ @h.link(:rel => 'stylesheet', :type =>'text/css', :href =>@css) if @css
30
+ @h.script(:type => 'text/javascript', :src => @js) do
31
+ end if @js
32
+ end
33
+ @h.body do
34
+ render_questions
35
+ end
36
+ end
37
+ end
38
+ self
39
+ end
40
+
41
+ def render_with_template
42
+ # local variables that should be in scope in the template
43
+ quiz = @quiz
44
+ title = "Quiz" unless @title
45
+ # the ERB template includes 'yield' where questions should go:
46
+ output = ERB.new(IO.read(File.expand_path @template)).result(binding)
47
+ @output = output
48
+ end
49
+
50
+ def render_questions
51
+ render_random_seed
52
+ @h.form do
53
+ @h.ol :class => 'questions' do
54
+ @quiz.questions.each_with_index do |q,i|
55
+ case q
56
+ when SelectMultiple then render_select_multiple(q,i)
57
+ when MultipleChoice, TrueFalse then render_multiple_choice(q,i)
58
+ when FillIn then render_fill_in(q, i)
59
+ else
60
+ raise "Unknown question type: #{q}"
61
+ end
62
+ end
63
+ end
64
+ @h.input(:type => 'submit', :value => 'Enviar')
65
+ end
66
+ end
67
+
68
+
69
+ def render_multiple_choice(q,index)
70
+ render_question_text(q, index) do
71
+ answers =
72
+ if q.class == TrueFalse then q.answers.sort.reverse # True always first
73
+ elsif q.randomize then q.answers.sort_by { rand }
74
+ else q.answers
75
+ end
76
+ @h.ol :class => 'answers' do
77
+ answers.each do |answer|
78
+ if @show_solutions
79
+ render_answer_for_solutions(answer, q.raw?)
80
+ else
81
+ #if q.raw? then @h.li { |l| l << answer.answer_text } else @h.li answer.answer_text end
82
+ @h.input(:type => 'radio', :name => 'a', :class => 'select') { |p|
83
+ p << answer.answer_text
84
+ p << '</br>'
85
+ }
86
+ end
87
+ end
88
+ end
89
+ end
90
+ self
91
+ end
92
+
93
+ def render_select_multiple(q,index)
94
+ render_question_text(q, index) do
95
+ answers =
96
+ if q.randomize then q.answers.sort_by { rand }
97
+ else q.answers
98
+ end
99
+ @h.ol :class => 'answers' do
100
+ answers.each do |answer|
101
+ if @show_solutions
102
+ render_answer_for_solutions(answer, q.raw?)
103
+ else
104
+ #if q.raw? then @h.li { |l| l << answer.answer_text } else @h.li answer.answer_text end
105
+ @h.input(:type => 'checkbox', :name => 'b', :class => 'check') { |p|
106
+ p << answer.answer_text
107
+ p << '</br>'
108
+ }
109
+ end
110
+ end
111
+ end
112
+ end
113
+ self
114
+ end
115
+
116
+ def render_fill_in(q, idx)
117
+ render_question_text(q, idx) do
118
+ if @show_solutions
119
+ answer = q.answers[0]
120
+ if answer.has_explanation?
121
+ if q.raw? then @h.p(:class => 'explanation') { |p| p << answer.explanation }
122
+ else @h.p(answer.explanation, :class => 'explanation') end
123
+ end
124
+ answers = (answer.answer_text.kind_of?(Array) ? answer.answer_text : [answer.answer_text])
125
+ @h.ol :class => 'answers' do
126
+ answers.each do |answer|
127
+ if answer.kind_of?(Regexp)
128
+ answer = answer.inspect
129
+ if !q.case_sensitive
130
+ answer += 'i'
131
+ end
132
+ end
133
+ @h.li do
134
+ if q.raw? then @h.p { |p| p << answer } else @h.p answer end
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
141
+
142
+ def render_answer_for_solutions(answer,raw)
143
+ args = {:class => (answer.correct? ? 'correct' : 'incorrect')}
144
+ @h.li(args) do
145
+ if raw then @h.p { |p| p << answer.answer_text } else @h.p answer.answer_text end
146
+ if answer.has_explanation?
147
+ if raw then @h.p(:class => 'explanation') { |p| p << answer.explanation }
148
+ else @h.p(answer.explanation, :class => 'explanation') end
149
+ end
150
+ end
151
+ end
152
+
153
+ def render_question_text(question,index)
154
+ html_args = {
155
+ :id => "question-#{index}",
156
+ :class => ['question', question.class.to_s.downcase, (question.multiple ? 'multiple' : '')]
157
+ .join(' ')
158
+ }
159
+ @h.li html_args do
160
+ @h.div :class => 'text' do
161
+ qtext = "[#{question.points} point#{'s' if question.points>1}] " <<
162
+ ('Select ALL that apply: ' if question.multiple).to_s <<
163
+ if question.class == FillIn then question.question_text.gsub(/\-+/, '')
164
+ else question.question_text
165
+ end
166
+ #if question.raw?
167
+ # @h.p { |p| p << qtext }
168
+ #else
169
+ qtext.each_line do |p|
170
+ @h.p do |par|
171
+ par << p # preserves HTML markup
172
+ @h.input(:type => 'text', :class => 'fillin') if (question.class == FillIn)
173
+ end
174
+ end
175
+ #end
176
+ end
177
+ yield # render answers
178
+ end
179
+ self
180
+ end
181
+
182
+ def quiz_header
183
+ @h.div(:id => 'student-name') do
184
+ @h.p 'Name:'
185
+ @h.p 'Student ID:'
186
+ end
187
+ if @quiz.options[:instructions]
188
+ @h.div :id => 'instructions' do
189
+ @quiz.options[:instructions].each_line { |p| @h.p p }
190
+ end
191
+ end
192
+ self
193
+ end
194
+
195
+ def render_random_seed
196
+ @h.comment! "Seed: #{@quiz.seed}"
197
+ end
198
+ end
@@ -6,13 +6,11 @@
6
6
  .header { text-align: right; font-weight: bold; padding-right: 30%; line-height: 300%; }
7
7
  h1 { text-align: center; }
8
8
  ol.questions { list-style-type: number; }
9
- li.question { page-break-inside: avoid; border-bottom: 1px solid grey; padding-bottom: 2ex; }
9
+ li.question { page-break-inside: avoid; border-bottom: 1px solid grey; }
10
10
  li.multiplechoice ol.answers { list-style-type: lower-alpha; }
11
- li.multiplechoice ol.answers li { padding-bottom: 0.5ex; }
12
11
  li.selectmultiple ol.answers { vertical-align: center; list-style-type: none; list-style-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUAQMAAAC3R49OAAAABlBMVEUAAAD///+l2Z/dAAAAEklEQVQImWNgAIL6/w+ogoEAAKI4Kp2NVIeDAAAAAElFTkSuQmCC'); }
13
12
  li.truefalse ol.answers { list-style-type: none; }
14
- li.truefalse ol.answers li { width: 15%; display: inline-block; }
15
- .correct { color: green }
13
+ .correct { color: green; font-weight: bold; border: 1px solid black; padding: 0.2ex; }
16
14
  .incorrect { color: red }
17
15
  .explanation { font-style: italic }
18
16
  .instructions { clear: both; border: 1px solid black; align: center; padding: 2ex; margin: 2ex; }
@@ -0,0 +1,44 @@
1
+ <html>
2
+ <head>
3
+ <title><%= quiz.title %></title>
4
+ <style type="text/css" media="all">
5
+ body { font-family: Times, serif; }
6
+ .header { text-align: right; font-weight: bold; padding-right: 30%; line-height: 300%; }
7
+ h1 { text-align: center; }
8
+ ol.questions { list-style-type: number; }
9
+ li.question { page-break-inside: avoid; border-bottom: 1px solid grey; padding-bottom: 2ex; }
10
+ li.multiplechoice ol.answers { list-style-type: lower-alpha; }
11
+ li.multiplechoice ol.answers li { padding-bottom: 0.5ex; }
12
+ li.selectmultiple ol.answers { vertical-align: center; list-style-type: none; list-style-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUAQMAAAC3R49OAAAABlBMVEUAAAD///+l2Z/dAAAAEklEQVQImWNgAIL6/w+ogoEAAKI4Kp2NVIeDAAAAAElFTkSuQmCC'); }
13
+ li.truefalse ol.answers { list-style-type: none; }
14
+ li.truefalse ol.answers li { width: 15%; display: inline-block; }
15
+ .correct { color: green }
16
+ .incorrect { color: red }
17
+ .explanation { font-style: italic }
18
+ .instructions { clear: both; border: 1px solid black; align: center; padding: 2ex; margin: 2ex; }
19
+ </style>
20
+ </head>
21
+ <body>
22
+ <div class="header">
23
+ <div id="name">Name:</div>
24
+ <div id="sid">SID:</div>
25
+ </div>
26
+ <h1><%= quiz.title %></h1>
27
+ <div class="instructions">
28
+ <ul>
29
+ <li>No books, notes, or electronic devices allowed. </li>
30
+ <li>Time limit is 30 minutes.</li>
31
+ <li><%= quiz.num_questions %> multiple-choice questions, points indicated per question,
32
+ <%= quiz.points %> points total. Points per question are intended
33
+ to reflect approximate times they should take, at about 1 point per minute.</li>
34
+ <li>For 'select all that apply' questions worth N points,
35
+ you get 1/N of the points for each RIGHT answer that you check, plus
36
+ 1/N of the points for each WRONG answer that you correctly
37
+ leave unchecked. That is, equal weight is given to deciding
38
+ whether each choice is part of the right answer or not.</li>
39
+ </ul>
40
+ <b>Good skill!</b>
41
+ </div>
42
+ <%= yield %>
43
+ </body>
44
+ </html>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-12-16 00:00:00.000000000 Z
12
+ date: 2014-05-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: builder
@@ -63,10 +63,12 @@ files:
63
63
  - lib/ruql/renderers/auto_qcm_renderer.rb
64
64
  - lib/ruql/renderers/edxml_renderer.rb
65
65
  - lib/ruql/renderers/html5_renderer.rb
66
+ - lib/ruql/renderers/html_form_renderer.rb
66
67
  - lib/ruql/renderers/json_renderer.rb
67
68
  - lib/ruql/renderers/xml_renderer.rb
68
69
  - templates/autoqcm.tex.erb
69
70
  - templates/html5.html.erb
71
+ - templates/htmlform.html.erb
70
72
  - bin/ruql
71
73
  homepage: http://github.com/saasbook/ruql
72
74
  licenses: