ruql 0.0.2 → 0.0.4

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