moodle2cc 0.0.1

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