moodle2cc 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.
Files changed (95) hide show
  1. data/.gitignore +20 -0
  2. data/Gemfile +4 -0
  3. data/Guardfile +14 -0
  4. data/LICENSE +661 -0
  5. data/README.md +38 -0
  6. data/Rakefile +12 -0
  7. data/bin/moodle2cc +6 -0
  8. data/lib/moodle2cc.rb +60 -0
  9. data/lib/moodle2cc/canvas/.converter.rb.swo +0 -0
  10. data/lib/moodle2cc/canvas/.wiki.rb.swo +0 -0
  11. data/lib/moodle2cc/canvas/assessment.rb +109 -0
  12. data/lib/moodle2cc/canvas/assignment.rb +108 -0
  13. data/lib/moodle2cc/canvas/converter.rb +28 -0
  14. data/lib/moodle2cc/canvas/course.rb +145 -0
  15. data/lib/moodle2cc/canvas/discussion_topic.rb +64 -0
  16. data/lib/moodle2cc/canvas/label.rb +8 -0
  17. data/lib/moodle2cc/canvas/question.rb +468 -0
  18. data/lib/moodle2cc/canvas/question_bank.rb +54 -0
  19. data/lib/moodle2cc/canvas/resource.rb +16 -0
  20. data/lib/moodle2cc/canvas/templates/assignment.html.erb +9 -0
  21. data/lib/moodle2cc/canvas/templates/syllabus.html.erb +9 -0
  22. data/lib/moodle2cc/canvas/templates/wiki_content.html.erb +10 -0
  23. data/lib/moodle2cc/canvas/web_content.rb +10 -0
  24. data/lib/moodle2cc/canvas/web_link.rb +17 -0
  25. data/lib/moodle2cc/canvas/wiki.rb +25 -0
  26. data/lib/moodle2cc/cc/assessment.rb +25 -0
  27. data/lib/moodle2cc/cc/assignment.rb +66 -0
  28. data/lib/moodle2cc/cc/cc_helper.rb +282 -0
  29. data/lib/moodle2cc/cc/converter.rb +136 -0
  30. data/lib/moodle2cc/cc/course.rb +16 -0
  31. data/lib/moodle2cc/cc/discussion_topic.rb +53 -0
  32. data/lib/moodle2cc/cc/label.rb +6 -0
  33. data/lib/moodle2cc/cc/question.rb +23 -0
  34. data/lib/moodle2cc/cc/resource.rb +30 -0
  35. data/lib/moodle2cc/cc/templates/assignment.html.erb +9 -0
  36. data/lib/moodle2cc/cc/templates/syllabus.html.erb +9 -0
  37. data/lib/moodle2cc/cc/templates/wiki_content.html.erb +10 -0
  38. data/lib/moodle2cc/cc/web_content.rb +40 -0
  39. data/lib/moodle2cc/cc/web_link.rb +53 -0
  40. data/lib/moodle2cc/cc/wiki.rb +72 -0
  41. data/lib/moodle2cc/cli.rb +14 -0
  42. data/lib/moodle2cc/error.rb +3 -0
  43. data/lib/moodle2cc/migrator.rb +23 -0
  44. data/lib/moodle2cc/moodle/backup.rb +38 -0
  45. data/lib/moodle2cc/moodle/course.rb +24 -0
  46. data/lib/moodle2cc/moodle/grade_item.rb +24 -0
  47. data/lib/moodle2cc/moodle/info.rb +8 -0
  48. data/lib/moodle2cc/moodle/mod.rb +127 -0
  49. data/lib/moodle2cc/moodle/question.rb +98 -0
  50. data/lib/moodle2cc/moodle/question_category.rb +17 -0
  51. data/lib/moodle2cc/moodle/section.rb +37 -0
  52. data/lib/moodle2cc/resource_factory.rb +32 -0
  53. data/lib/moodle2cc/version.rb +3 -0
  54. data/moodle2cc.gemspec +29 -0
  55. data/test/acceptance/migrator_test.rb +23 -0
  56. data/test/fixtures/cc.imscc +0 -0
  57. data/test/fixtures/moodle_backup/course_files/folder/test.txt +1 -0
  58. data/test/fixtures/moodle_backup/course_files/test.txt +1 -0
  59. data/test/fixtures/moodle_backup/moodle.xml +916 -0
  60. data/test/test_helper.rb +43 -0
  61. data/test/test_question_helper.rb +107 -0
  62. data/test/test_wiki_helper.rb +21 -0
  63. data/test/unit/canvas/assessment_test.rb +218 -0
  64. data/test/unit/canvas/assignment_test.rb +220 -0
  65. data/test/unit/canvas/converter_test.rb +159 -0
  66. data/test/unit/canvas/course_test.rb +176 -0
  67. data/test/unit/canvas/discussion_topic_test.rb +129 -0
  68. data/test/unit/canvas/label_test.rb +32 -0
  69. data/test/unit/canvas/question_bank_test.rb +73 -0
  70. data/test/unit/canvas/question_test.rb +824 -0
  71. data/test/unit/canvas/web_content_test.rb +37 -0
  72. data/test/unit/canvas/web_link_test.rb +48 -0
  73. data/test/unit/canvas/wiki_test.rb +74 -0
  74. data/test/unit/cc/assessment_test.rb +48 -0
  75. data/test/unit/cc/assignment_test.rb +67 -0
  76. data/test/unit/cc/cc_helper_test.rb +19 -0
  77. data/test/unit/cc/converter_test.rb +159 -0
  78. data/test/unit/cc/course_test.rb +36 -0
  79. data/test/unit/cc/discussion_topic_test.rb +83 -0
  80. data/test/unit/cc/label_test.rb +29 -0
  81. data/test/unit/cc/question_test.rb +49 -0
  82. data/test/unit/cc/web_content_test.rb +76 -0
  83. data/test/unit/cc/web_link_test.rb +79 -0
  84. data/test/unit/cc/wiki_test.rb +139 -0
  85. data/test/unit/migrator_test.rb +56 -0
  86. data/test/unit/moodle/backup_test.rb +30 -0
  87. data/test/unit/moodle/course_test.rb +57 -0
  88. data/test/unit/moodle/grade_item_test.rb +72 -0
  89. data/test/unit/moodle/info_test.rb +21 -0
  90. data/test/unit/moodle/mod_test.rb +301 -0
  91. data/test/unit/moodle/question_category_test.rb +34 -0
  92. data/test/unit/moodle/question_test.rb +185 -0
  93. data/test/unit/moodle/section_test.rb +75 -0
  94. data/test/unit/resource_factory_test.rb +119 -0
  95. metadata +342 -0
@@ -0,0 +1,64 @@
1
+ module Moodle2CC::Canvas
2
+ class DiscussionTopic < Moodle2CC::CC::DiscussionTopic
3
+ include Resource
4
+ attr_accessor :posted_at, :position, :type, :identifierref
5
+
6
+ def initialize(mod, position=0)
7
+ super
8
+ @text = convert_file_path_tokens(mod.intro)
9
+
10
+ if mod.section_mod && mod.section_mod.added.to_i > 0
11
+ @posted_at = ims_datetime(Time.at(mod.section_mod.added))
12
+ end
13
+
14
+ @position = position
15
+ @type = 'topic'
16
+ @identifierref = create_key(@id, 'topic_meta_')
17
+ end
18
+
19
+ def create_resource_node(resources_node)
20
+ super
21
+ resources_node.resource(
22
+ :type => LOR,
23
+ :identifier => @identifierref,
24
+ :href => "#{@identifierref}.xml"
25
+ ) do |resource_node|
26
+ resource_node.file(:href => "#{@identifierref}.xml")
27
+ end
28
+ end
29
+
30
+ def create_resource_sub_nodes(resource_node)
31
+ resource_node.dependency(:identifierref => @identifierref)
32
+ end
33
+
34
+ def create_files(export_dir)
35
+ super
36
+ create_topic_meta_xml(export_dir)
37
+ end
38
+
39
+ def create_topic_meta_xml(export_dir)
40
+ path = File.join(export_dir, "#{@identifierref}.xml")
41
+ File.open(path, 'w') do |file|
42
+ document = Builder::XmlMarkup.new(:target => file, :indent => 2)
43
+ document.instruct!
44
+ document.topicMeta(
45
+ :identifier => @identifierref,
46
+ 'xsi:schemaLocation' => "http://canvas.instructure.com/xsd/cccv1p0 http://canvas.instructure.com/xsd/cccv1p0.xsd",
47
+ 'xmlns:xsi' => "http://www.w3.org/2001/XMLSchema-instance",
48
+ 'xmlns' => "http://canvas.instructure.com/xsd/cccv1p0"
49
+ ) do |topic_meta|
50
+ topic_meta.topic_id identifier
51
+ topic_meta.title @title
52
+ topic_meta.posted_at @posted_at
53
+ topic_meta.position @position
54
+ topic_meta.type @type
55
+ end
56
+ end
57
+ end
58
+
59
+ def create_module_meta_item_elements(item_node)
60
+ item_node.content_type 'DiscussionTopic'
61
+ item_node.identifierref @identifier
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,8 @@
1
+ module Moodle2CC::Canvas
2
+ class Label < Moodle2CC::CC::Label
3
+ include Resource
4
+ def create_module_meta_item_elements(item_node)
5
+ item_node.content_type 'ContextModuleSubHeader'
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,468 @@
1
+ module Moodle2CC::Canvas
2
+ class Question < Moodle2CC::CC::Question
3
+ META_ATTRIBUTES = [:question_type, :points_possible, :assessment_question_identifierref]
4
+ QUESTION_TYPE_MAP = {
5
+ 'calculated' => 'calculated_question',
6
+ 'description' => 'text_only_question',
7
+ 'essay' => 'essay_question',
8
+ 'match' => 'matching_question',
9
+ 'multianswer' => 'embedded_answers_question',
10
+ 'multichoice' => 'multiple_choice_question',
11
+ 'shortanswer' => 'short_answer_question',
12
+ 'numerical' => 'numerical_question',
13
+ 'truefalse' => 'true_false_question',
14
+ 'choice' => 'multiple_choice_question'
15
+ }
16
+ QUESTION_TYPE_ID_MAP = {
17
+ 1 => 'true_false_question', # yes/no question
18
+ 2 => 'essay_question', # text box question
19
+ 3 => 'essay_question', # essay box question
20
+ 4 => 'multiple_choice_question', # radio buttons question
21
+ 5 => 'multiple_answers_question', # check boxes question
22
+ 6 => 'multiple_choice_question', # dropdown box question
23
+ 8 => 'multiple_dropdowns_question', # rate 1..5 question
24
+ 9 => 'essay_question', # date question
25
+ 10 => 'numerical_question', # numeric question
26
+ 100 => 'text_only_question', # label question
27
+ }
28
+
29
+ attr_accessor :material, :general_feedback, :answer_tolerance,
30
+ :formulas, :formula_decimal_places, :vars, :var_sets, :matches, :answers,
31
+ :numericals, :length, *META_ATTRIBUTES
32
+
33
+ def initialize(question, assessment=nil)
34
+ super
35
+ @question_type = question.type ? QUESTION_TYPE_MAP[question.type] : QUESTION_TYPE_ID_MAP[question.type_id]
36
+ @points_possible = question.grade
37
+ @general_feedback = question.general_feedback
38
+ @length = question.length
39
+
40
+ @answers = []
41
+
42
+ unless question.answers.empty?
43
+ @answers = question.answers.map do |answer|
44
+ Moodle2CC::OpenStruct.new(
45
+ :id => answer.id,
46
+ :text => answer.text,
47
+ :fraction => answer.fraction,
48
+ :feedback => answer.feedback
49
+ )
50
+ end
51
+ end
52
+
53
+ unless question.choices.nil? || question.choices.empty?
54
+ @answers = question.choices.map do |answer|
55
+ Moodle2CC::OpenStruct.new(
56
+ :id => answer.id,
57
+ :text => answer.content
58
+ )
59
+ end
60
+ end
61
+
62
+ calculation = question.calculations.first unless question.calculations.nil?
63
+ if calculation
64
+ @answer_tolerance = calculation.tolerance
65
+ @formula_decimal_places = calculation.correct_answer_format == 1 ? calculation.correct_answer_length : 0
66
+ @formulas = question.answers.map { |a| a.text.gsub(/[\{\}\s]/, '') }
67
+ @vars = calculation.dataset_definitions.map do |ds_def|
68
+ name = ds_def.name
69
+ type, min, max, scale = ds_def.options.split(':')
70
+ {:name => name, :min => min, :max => max, :scale => scale}
71
+ end
72
+ @var_sets = []
73
+ calculation.dataset_definitions.each do |ds_def|
74
+ ds_def.dataset_items.sort_by(&:number).each_with_index do |ds_item, index|
75
+ var_set = @var_sets[index] || {}
76
+ vars = var_set[:vars] || {}
77
+ vars[ds_def.name] = ds_item.value
78
+ var_set[:vars] = vars
79
+ @var_sets[index] = var_set
80
+ end
81
+ end
82
+ @var_sets.map do |var_set|
83
+ answer = 0
84
+ var_set[:vars].each do |k, v|
85
+ answer += v
86
+ end
87
+ var_set[:answer] = answer
88
+ end
89
+ end
90
+
91
+ unless question.numericals.nil?
92
+ @numericals = question.numericals.map do |n|
93
+ Moodle2CC::OpenStruct.new(
94
+ :answer => @answers.find { |a| a.id == n.answer_id },
95
+ :tolerance => n.tolerance
96
+ )
97
+ end
98
+ end
99
+
100
+ if !question.matches.nil? && question.matches.length > 0
101
+ answers = question.matches.inject({}) do |result, match|
102
+ result[match.code] = match.answer_text
103
+ result
104
+ end
105
+ @matches = question.matches.select do |match|
106
+ match.question_text && match.question_text.strip.length > 0
107
+ end.map do |match|
108
+ Moodle2CC::OpenStruct.new(
109
+ :question => match.question_text,
110
+ :answers => answers,
111
+ :answer => match.code
112
+ )
113
+ end
114
+ end
115
+
116
+ material = question.text
117
+ material = question.content || '' if material.nil?
118
+ @material = material.gsub(/\{(.*?)\}/, '[\1]')
119
+ if @question_type == 'multiple_dropdowns_question' && !@answers.empty?
120
+ responses = @answers[@length..-1]
121
+ responses.each_with_index do |response, index|
122
+ @material << "\n#{response.text} [response#{index + 1}]"
123
+ end
124
+ end
125
+
126
+ if question.instance_id
127
+ @assessment_question_identifierref = create_key(question.id, "#{@identifier_prefix}question_")
128
+ end
129
+ end
130
+
131
+ def create_item_xml(section_node)
132
+ section_node.item(:title => @title, :ident => @identifier) do |item_node|
133
+ item_node.itemmetadata do |meta_node|
134
+ meta_node.qtimetadata do |qtime_node|
135
+ META_ATTRIBUTES.each do |attr|
136
+ value = send(attr).to_s
137
+ if value && value.length > 0
138
+ qtime_node.qtimetadatafield do |field_node|
139
+ field_node.fieldlabel attr.to_s
140
+ field_node.fieldentry value
141
+ end
142
+ end
143
+ end
144
+ end
145
+ end
146
+
147
+ item_node.presentation do |presentation_node|
148
+ presentation_node.material do |material_node|
149
+ material_node.mattext(@material, :texttype => 'text/html')
150
+ end
151
+ create_responses(presentation_node)
152
+ end
153
+
154
+ item_node.resprocessing do |processing_node|
155
+ processing_node.outcomes do |outcomes_node|
156
+ outcomes_node.decvar(:maxvalue => '100', :minvalue => '0', :varname => 'SCORE', :vartype => 'Decimal')
157
+ end
158
+ if @general_feedback
159
+ processing_node.respcondition(:continue => 'Yes') do |condition_node|
160
+ condition_node.conditionvar do |var_node|
161
+ var_node.other
162
+ end
163
+ condition_node.displayfeedback(:feedbacktype => 'Response', :linkrefid => 'general_fb')
164
+ end
165
+ end
166
+ create_response_conditions(processing_node)
167
+ end
168
+
169
+ create_feedback_nodes(item_node)
170
+ create_additional_nodes(item_node)
171
+ end
172
+ end
173
+
174
+ def create_responses(presentation_node)
175
+ case @question_type
176
+ when 'calculated_question'
177
+ presentation_node.response_str(:rcardinality => 'Single', :ident => 'response1') do |response_node|
178
+ response_node.render_fib(:fibtype => 'Decimal') do |render_node|
179
+ render_node.response_label(:ident => 'answer1')
180
+ end
181
+ end
182
+ when 'essay_question', 'short_answer_question', 'embedded_answers_question'
183
+ presentation_node.response_str(:rcardinality => 'Single', :ident => 'response1') do |response_node|
184
+ response_node.render_fib do |render_node|
185
+ render_node.response_label(:ident => 'answer1', :rshuffle => 'No')
186
+ end
187
+ end
188
+ when 'matching_question'
189
+ @matches.each do |match|
190
+ presentation_node.response_lid(:ident => "response_#{match.answer}") do |response_node|
191
+ response_node.material do |material_node|
192
+ material_node.mattext(match.question, :texttype => 'text/plain')
193
+ end
194
+ response_node.render_choice do |choice_node|
195
+ match.answers.each do |ident, text|
196
+ choice_node.response_label(:ident => ident) do |label_node|
197
+ label_node.material do |material_node|
198
+ material_node.mattext text
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end
204
+ end
205
+ when 'multiple_choice_question'
206
+ presentation_node.response_lid(:ident => 'response1', :rcardinality => 'Single') do |response_node|
207
+ response_node.render_choice do |choice_node|
208
+ @answers.each do |answer|
209
+ choice_node.response_label(:ident => answer.id) do |label_node|
210
+ label_node.material do |material_node|
211
+ material_node.mattext answer.text, :texttype => 'text/plain'
212
+ end
213
+ end
214
+ end
215
+ end
216
+ end
217
+ when 'multiple_answers_question'
218
+ presentation_node.response_lid(:ident => 'response1', :rcardinality => 'Multiple') do |response_node|
219
+ response_node.render_choice do |choice_node|
220
+ @answers.each do |answer|
221
+ choice_node.response_label(:ident => answer.id) do |label_node|
222
+ label_node.material do |material_node|
223
+ material_node.mattext answer.text, :texttype => 'text/plain'
224
+ end
225
+ end
226
+ end
227
+ end
228
+ end
229
+ when 'multiple_dropdowns_question'
230
+ answers = @answers[0...@length]
231
+ responses = @answers[@length..-1]
232
+ responses.each_with_index do |response, index|
233
+ response_id = index + 1
234
+ presentation_node.response_lid(:ident => "response_response#{response_id}") do |response_node|
235
+ response_node.material do |material_node|
236
+ material_node.mattext "response#{response_id}"
237
+ end
238
+ response_node.render_choice do |choice_node|
239
+ answers.each do |answer|
240
+ choice_node.response_label(:ident => "#{response_id}#{answer.id}") do |label_node|
241
+ label_node.material do |material_node|
242
+ material_node.mattext answer.text, :texttype => 'text/plain'
243
+ end
244
+ end
245
+ end
246
+ end
247
+ end
248
+ end
249
+ when 'numerical_question'
250
+ presentation_node.response_str(:rcardinality => 'Single', :ident => 'response1') do |response_node|
251
+ response_node.render_fib(:fibtype => 'Decimal') do |render_fib_node|
252
+ render_fib_node.response_label(:ident => 'answer1')
253
+ end
254
+ end
255
+ when 'true_false_question'
256
+ presentation_node.response_lid(:rcardinality => 'Single', :ident => 'response1') do |response_node|
257
+ response_node.render_choice do |render_choice_node|
258
+ @answers.each do |answer|
259
+ render_choice_node.response_label(:ident => answer.id) do |response_label_node|
260
+ response_label_node.material do |material_node|
261
+ material_node.mattext(answer.text, :texttype => 'text/plain')
262
+ end
263
+ end
264
+ end
265
+ end
266
+ end
267
+ end
268
+ end
269
+
270
+ def create_response_conditions(processing_node)
271
+ case @question_type
272
+ when 'calculated_question'
273
+ processing_node.respcondition(:title => 'correct') do |condition|
274
+ condition.conditionvar do |var_node|
275
+ var_node.other
276
+ end
277
+ condition.setvar('100', :varname => 'SCORE', :action => 'Set')
278
+ end
279
+ processing_node.respcondition(:title => 'incorrect') do |condition|
280
+ condition.conditionvar do |var_node|
281
+ var_node.other
282
+ end
283
+ condition.setvar('0', :varname => 'SCORE', :action => 'Set')
284
+ end
285
+ when 'essay_question', 'embedded_answers_question'
286
+ processing_node.respcondition(:continue => 'No') do |condition|
287
+ condition.conditionvar do |var_node|
288
+ var_node.other
289
+ end
290
+ end
291
+ when 'matching_question'
292
+ score = 100.0 / @matches.length.to_f
293
+ @matches.each do |match|
294
+ processing_node.respcondition do |condition_node|
295
+ condition_node.conditionvar do |var_node|
296
+ var_node.varequal match.answer, :respident => "response_#{match.answer}"
297
+ end
298
+ condition_node.setvar "%.2f" % score, :varname => 'SCORE', :action => 'Add'
299
+ end
300
+ end
301
+ when 'multiple_choice_question'
302
+ # Feeback
303
+ @answers.each do |answer|
304
+ next unless answer.feedback && answer.feedback.strip.length > 0
305
+ processing_node.respcondition(:continue => 'Yes') do |condition_node|
306
+ condition_node.conditionvar do |var_node|
307
+ var_node.varequal answer.id, :respident => 'response1'
308
+ end
309
+ condition_node.displayfeedback(:feedbacktype => 'Response', :linkrefid => "#{answer.id}_fb")
310
+ end
311
+ end
312
+
313
+ # Scores
314
+ @answers.each do |answer|
315
+ processing_node.respcondition(:continue => 'No') do |condition_node|
316
+ condition_node.conditionvar do |var_node|
317
+ var_node.varequal answer.id, :respident => 'response1'
318
+ end
319
+ condition_node.setvar(get_score(answer.fraction), :varname => 'SCORE', :action => 'Set')
320
+ end
321
+ end
322
+ when 'multiple_answers_question'
323
+ processing_node.respcondition(:continue => 'No') do |condition_node|
324
+ first = @answers.first
325
+ rest = @answers[1..-1]
326
+ condition_node.conditionvar do |var_node|
327
+ var_node.and do |and_node|
328
+ and_node.varequal first.id, :respident => 'response1'
329
+ rest.each do |answer|
330
+ and_node.not do |not_node|
331
+ not_node.varequal answer.id, :respident => 'response1'
332
+ end
333
+ end
334
+ end
335
+ end
336
+ condition_node.setvar('100', :varname => 'SCORE', :action => 'Set')
337
+ end
338
+ when 'multiple_dropdowns_question'
339
+ answers = @answers[0...@length]
340
+ responses = @answers[@length..-1]
341
+ score = 100.0 / responses.length.to_f
342
+ responses.each_with_index do |response, index|
343
+ response_id = index + 1
344
+ processing_node.respcondition do |condition_node|
345
+ condition_node.conditionvar do |var_node|
346
+ var_node.varequal "#{response_id}#{answers.first.id}", :respident => "response_response#{response_id}"
347
+ end
348
+ condition_node.setvar "%.2f" % score, :varname => 'SCORE', :action => 'Add'
349
+ end
350
+ end
351
+ when 'numerical_question'
352
+ @numericals.each do |numerical|
353
+ processing_node.respcondition(:continue => 'No') do |condition_node|
354
+ condition_node.conditionvar do |var_node|
355
+ var_node.or do |or_node|
356
+ or_node.varequal numerical.answer.text, :respident => 'response1'
357
+ or_node.and do |and_node|
358
+ and_node.vargte numerical.answer.text.to_f - numerical.tolerance.to_f, :respident => 'response1'
359
+ and_node.varlte numerical.answer.text.to_f + numerical.tolerance.to_f, :respident => 'response1'
360
+ end
361
+ end
362
+ end
363
+ condition_node.setvar(get_score(numerical.answer.fraction), :varname => "SCORE", :action => 'Set')
364
+ condition_node.displayfeedback(:feedbacktype => 'Response', :linkrefid => "#{numerical.answer.id}_fb") if numerical.answer.feedback && numerical.answer.feedback.strip.length > 0
365
+ end
366
+ end
367
+ when 'short_answer_question'
368
+ # Feeback
369
+ @answers.each do |answer|
370
+ next unless answer.feedback && answer.feedback.strip.length > 0
371
+ processing_node.respcondition(:continue => 'Yes') do |condition_node|
372
+ condition_node.conditionvar do |var_node|
373
+ var_node.varequal answer.text, :respident => 'response1'
374
+ end
375
+ condition_node.displayfeedback(:feedbacktype => 'Response', :linkrefid => "#{answer.id}_fb")
376
+ end
377
+ end
378
+
379
+ # Conditions
380
+ @answers.each do |answer|
381
+ processing_node.respcondition(:continue => 'No') do |condition_node|
382
+ condition_node.conditionvar do |var_node|
383
+ var_node.varequal answer.text, :respident => 'response1'
384
+ end
385
+ condition_node.setvar(get_score(answer.fraction), :varname => 'SCORE', :action => "Set")
386
+ end
387
+ end
388
+ when 'true_false_question'
389
+ @answers.each do |answer|
390
+ processing_node.respcondition(:continue => 'No') do |condition_node|
391
+ condition_node.conditionvar do |var_node|
392
+ var_node.varequal answer.id, :respident => 'response1'
393
+ end
394
+ condition_node.displayfeedback(:feedbacktype => 'Response', :linkrefid => "#{answer.id}_fb") if answer.feedback && answer.feedback.strip.length > 0
395
+ condition_node.setvar(get_score(answer.fraction), :varname => 'SCORE', :action => "Set")
396
+ end
397
+ end
398
+ end
399
+ end
400
+
401
+ def create_feedback_nodes(item_node)
402
+ # Feeback
403
+ if @general_feedback
404
+ item_node.itemfeedback(:ident => 'general_fb') do |fb_node|
405
+ fb_node.flow_mat do |flow_node|
406
+ flow_node.material do |material_node|
407
+ material_node.mattext(@general_feedback, :texttype => 'text/plain')
408
+ end
409
+ end
410
+ end
411
+ end
412
+
413
+ case @question_type
414
+ when 'multiple_choice_question', 'numerical_question', 'short_answer_question', 'true_false_question'
415
+ @answers.each do |answer|
416
+ next unless answer.feedback && answer.feedback.strip.length > 0
417
+ item_node.itemfeedback(:ident => "#{answer.id}_fb") do |feedback_node|
418
+ feedback_node.flow_mat do |flow_node|
419
+ flow_node.material do |material_node|
420
+ material_node.mattext answer.feedback, :texttype => 'text/plain'
421
+ end
422
+ end
423
+ end
424
+ end
425
+ end
426
+ end
427
+
428
+ def create_additional_nodes(item_node)
429
+ case @question_type
430
+ when 'calculated_question'
431
+ item_node.itemproc_extension do |extension_node|
432
+ extension_node.calculated do |calculated_node|
433
+ calculated_node.answer_tolerance @answer_tolerance
434
+ calculated_node.formulas(:decimal_places => @formula_decimal_places) do |formulas_node|
435
+ @formulas.each do |formula|
436
+ formulas_node.formula formula
437
+ end
438
+ end
439
+ calculated_node.vars do |vars_node|
440
+ @vars.each do |var|
441
+ vars_node.var(:name => var[:name], :scale => var[:scale]) do |var_node|
442
+ var_node.min var[:min]
443
+ var_node.max var[:max]
444
+ end
445
+ end
446
+ end
447
+ calculated_node.var_sets do |var_sets_node|
448
+ @var_sets.each do |var_set|
449
+ ident = var_set[:vars].sort.map { |k,v| v.to_s.split('.').join }.flatten.join
450
+ var_sets_node.var_set(:ident => ident) do |var_set_node|
451
+ var_set[:vars].each do |k, v|
452
+ var_set_node.var(v, :name => k)
453
+ end
454
+ var_set_node.answer var_set[:answer]
455
+ end
456
+ end
457
+ end
458
+ end
459
+ end
460
+ end
461
+ end
462
+
463
+ def get_score(fraction)
464
+ return 100 if fraction.nil?
465
+ (100 * fraction).to_i
466
+ end
467
+ end
468
+ end