asciidoctor-moodle 1.0.0.dev

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.
@@ -0,0 +1,162 @@
1
+ require_relative '../extensions'
2
+ module Asciidoctor
3
+ module Question
4
+ class DDTAndMWBlockProcessor < Extensions::BaseProcessor
5
+
6
+ def process(parent, source, tag)
7
+ docName = parent.attributes['docname']
8
+ id = tag[:id]
9
+ err = nil
10
+ subTypeQuest = tag[:type]
11
+ question = createQuestion(source.lines, docName,id, subTypeQuest)
12
+
13
+
14
+ stringQuest = question.join(" ")
15
+ numberQuest= 0
16
+ copySQ = stringQuest.clone
17
+
18
+
19
+ if subTypeQuest == "DDT" or subTypeQuest =="ddt"
20
+ ans = prepare_dragbox(copySQ)
21
+ else
22
+ ans =prepare_option(copySQ)
23
+ end
24
+
25
+ stringQuest.gsub! /\*(.+?)\*/ do |value|
26
+ numberQuest+=1
27
+ prepare_question value.gsub('*', ''), numberQuest
28
+ end
29
+ stringQuest << ans
30
+
31
+ new_parent = Asciidoctor::Block.new parent, :open, {:attributes => {'id' => "#{docName}question_#{id}_#{subTypeQuest}"}}
32
+
33
+
34
+ reader = Asciidoctor::Reader.new stringQuest
35
+
36
+
37
+ loop do
38
+ block = Asciidoctor::Parser.next_block reader, new_parent
39
+
40
+ break if block.nil?
41
+
42
+ if block.context == :listing
43
+
44
+ block.subs.push :macros
45
+ block.subs.push :quotes
46
+ end
47
+ new_parent.blocks.push block
48
+ end
49
+
50
+ if err.nil?
51
+
52
+ post_answers new_parent, tag
53
+ else
54
+ process_error_push new_parent, err, source.lines
55
+ end
56
+
57
+ new_parent
58
+ end
59
+
60
+ end
61
+
62
+
63
+
64
+ class MOODLEDDTAndMWBlockProcessor < DDTAndMWBlockProcessor
65
+
66
+
67
+ def createQuestion (question, docName, id, subTypeQuest)
68
+ if subTypeQuest == 'DDT' or subTypeQuest == 'ddt'
69
+ type="ddwtos"
70
+ else
71
+ type = "gapselect"
72
+ end
73
+ preQuest = " <question type=\"#{type}\">
74
+ <name>
75
+ <text>#{docName}_question_#{id}_#{subTypeQuest}</text>
76
+ </name>
77
+ <questiontext format=\"html\">
78
+ <text><![CDATA[ <p>"
79
+
80
+ posQuest = "</p> ]]></text>
81
+ </questiontext>
82
+ <generalfeedback format=\"html\">
83
+ <text></text>
84
+ </generalfeedback>
85
+ <defaultgrade>1</defaultgrade>
86
+ <penalty>0.3333333</penalty>
87
+ <hidden>0</hidden>
88
+ <idnumber></idnumber>
89
+ <shuffleanswers>1</shuffleanswers>
90
+ <correctfeedback format=\"html\">
91
+ <text><![CDATA[<p>Your answer is correct.</p>]]></text>
92
+ </correctfeedback>
93
+ <partiallycorrectfeedback format=\"html\">
94
+ <text><![CDATA[<p>Your answer is partially correct.</p>]]></text>
95
+ </partiallycorrectfeedback>
96
+ <incorrectfeedback format=\"html\">
97
+ <text><![CDATA[<p>Your answer is incorrect.</p>]]></text>
98
+ </incorrectfeedback>
99
+ <shownumcorrect/>"
100
+ question = [question.join("</p> <p> ")] #Create the newline
101
+
102
+ question.prepend(preQuest)
103
+ question.prepend("+++")
104
+
105
+ question.push(posQuest)
106
+ question.push("+++")
107
+ end
108
+
109
+ def prepare_question(value, tag)
110
+ "[[#{tag}]]"
111
+ end
112
+
113
+
114
+ def recup_ans(copySQ)
115
+ answers = Array.new
116
+ regex = %r{(\*.+?\*)}
117
+ matchData = regex.match(copySQ)
118
+ begin
119
+ answers.push matchData.captures[0]
120
+ copySQ.gsub!(matchData.captures[0],"")
121
+ matchData = regex.match(copySQ)
122
+ end while matchData != nil
123
+
124
+ answers.each do |value|
125
+ value.gsub!("*","")
126
+ end
127
+ answers
128
+ end
129
+
130
+
131
+ def prepare_dragbox(copySQ)
132
+ answers = recup_ans(copySQ)
133
+ dragbox ="+++"
134
+ answers.each do |val|
135
+ dragbox << "<dragbox>
136
+ <text>#{val}</text>
137
+ <group>1</group>
138
+ </dragbox> \n"
139
+ end
140
+ dragbox <<"</question> +++"
141
+ end
142
+
143
+ def prepare_option(copySQ)
144
+ answers = recup_ans(copySQ)
145
+ dragbox ="+++"
146
+ answers.each do |val|
147
+ dragbox << " <selectoption>
148
+ <text>#{val}</text>
149
+ <group>1</group>
150
+ </selectoption> \n"
151
+ end
152
+ dragbox <<"</question> +++"
153
+ end
154
+
155
+ def post_answers(parent, tag)
156
+
157
+ end
158
+
159
+ end
160
+
161
+ end
162
+ end
@@ -0,0 +1,34 @@
1
+ require 'asciidoctor' unless defined? ::Asciidoctor::VERSION
2
+ require 'asciidoctor/extensions'
3
+
4
+ require_relative 'version'
5
+
6
+ module Asciidoctor
7
+ module Question
8
+ module Extensions
9
+ class BaseProcessor < Asciidoctor::Extensions::BlockProcessor
10
+ use_dsl
11
+
12
+ def self.inherited(subclass)
13
+ subclass.option :contexts, [:example, :literal, :open]
14
+ subclass.option :content_model, :simple
15
+ end
16
+
17
+ def process_error(parent, err, source_lines)
18
+ lines = ['[NOTE]', '====', 'Error ! ' + err, '====']
19
+ block = Asciidoctor::Parser.next_block Asciidoctor::Reader.new(lines), parent
20
+ block.blocks.push Asciidoctor::Parser.next_block Asciidoctor::Reader.new(['[source, asciidoc]', '----'] + source_lines + ['----']), block
21
+ block
22
+ end
23
+
24
+ def process_error_push(parent, err, source_lines)
25
+ parent.blocks.push process_error parent, err, source_lines
26
+ end
27
+
28
+ def post_answers(parent, tag)
29
+ parent
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,93 @@
1
+ require_relative '../extensions'
2
+
3
+ module Asciidoctor
4
+ module Question
5
+ class GAPBlockProcessor < Extensions::BaseProcessor
6
+
7
+ def process(parent, source, tag)
8
+ docName = parent.attributes['docname']
9
+ id = tag[:id]
10
+ err = nil
11
+
12
+ question = prepare_question(source.lines, docName,id)
13
+ stringQuest = question.join(" ")
14
+
15
+ stringQuest.gsub! /__([^_]+?)__/ do |value|
16
+ prepare_gap value.gsub('_', ''), tag
17
+ end
18
+
19
+ new_parent = Asciidoctor::Block.new parent, :open, {:attributes => {'id' => "#{docName}question_#{id}_gap"}}
20
+
21
+
22
+ reader = Asciidoctor::Reader.new stringQuest
23
+
24
+
25
+ loop do
26
+ block = Asciidoctor::Parser.next_block reader, new_parent
27
+
28
+ break if block.nil?
29
+
30
+ if block.context == :listing
31
+
32
+ block.subs.push :macros
33
+ block.subs.push :quotes
34
+ end
35
+ new_parent.blocks.push block
36
+ end
37
+
38
+ if err.nil?
39
+
40
+ post_answers new_parent, tag
41
+ else
42
+ process_error_push new_parent, err, source.lines
43
+ end
44
+
45
+ new_parent
46
+ end
47
+
48
+
49
+ def prepare_gap(value, tag)
50
+ value
51
+ end
52
+ end
53
+
54
+
55
+
56
+ class MOODLEGAPBlockProcessor < GAPBlockProcessor
57
+
58
+ def prepare_gap(value, tag)
59
+ " {1:SHORTANSWER:%100%#{value}} "
60
+ end
61
+
62
+ def prepare_question(question, docName, id)
63
+ preQuest = " <question type=\"cloze\">
64
+ <name>
65
+ <text>#{docName}_question_#{id}_gap </text>
66
+ </name>
67
+ <questiontext format=\"html\">
68
+ <text><![CDATA[ "
69
+
70
+ posQuest = " ]]></text>
71
+ </questiontext>
72
+ <generalfeedback format=\"html\">
73
+ <text></text>
74
+ </generalfeedback>
75
+ <penalty>0.3333333</penalty>
76
+ <hidden>0</hidden>
77
+ <idnumber></idnumber>
78
+ </question>"
79
+
80
+
81
+ question.prepend("+++")
82
+ question.prepend(preQuest)
83
+ question.prepend("+++")
84
+ question.push("+++")
85
+ question.push(posQuest)
86
+ question.push("+++")
87
+
88
+ end
89
+
90
+ end
91
+
92
+ end
93
+ end
@@ -0,0 +1,160 @@
1
+ require_relative '../extensions'
2
+ #On peut utiliser l'italique ou le gras pour la question
3
+ module Asciidoctor
4
+ module Question
5
+
6
+ class MatchingBlockProcessor < Extensions::BaseProcessor
7
+
8
+ def nbQuestion(lines)
9
+ nbQuest = 0
10
+ lines.map do |answer|
11
+ if quest =~ /^-\s?/
12
+ nbQuest +=1
13
+ end
14
+ end
15
+ nbQuest
16
+ end
17
+
18
+
19
+ def splitQuestAndSubQuest(lines)
20
+ quest = Array.new
21
+ subQuest = Array.new
22
+ nbSubQuest = 0
23
+
24
+ lines.each do |line|
25
+ switch = true if line =~ /(^-|^\*)\s/
26
+ if switch
27
+ line.sub!(/(^-|^\*)\s/, "")
28
+ nbSubQuest +=1
29
+ subQuest.push line
30
+ else
31
+ quest.push line
32
+ end
33
+
34
+ end
35
+ [nbSubQuest, quest, subQuest]
36
+ end
37
+
38
+
39
+ def process(parent, source, tag)
40
+
41
+ size = tag.size
42
+ docName = parent.attributes['docname']
43
+ id = tag[:id]
44
+ err = '+++ You need at least 2 questions and one answer for each of them! +++'
45
+ lines = source.lines #Copy of the remaining Array of String lines managed by the reader
46
+ nbSubQuest, quest, subQuest = splitQuestAndSubQuest(lines)
47
+
48
+ answers = Array.new
49
+ tag.each { |key, val| answers.push val if key.class==Integer && key>2 && key< size-3} #Get the tag's answers
50
+ nbAns = answers.size
51
+
52
+ quest = prepare_question_lines(quest, docName, id, nbSubQuest)
53
+
54
+ subQuestAndAns = prepare_subQuest subQuest, answers
55
+
56
+
57
+ new_parent = Asciidoctor::Block.new parent, :open, {:attributes => {'id' => "#{docName}_question_#{id}_matching"}}
58
+
59
+
60
+ if nbAns>= nbSubQuest && nbAns>=2 && nbSubQuest>=2
61
+ reader = Asciidoctor::Reader.new(quest)
62
+ loop do
63
+ block = Asciidoctor::Parser.next_block reader, new_parent
64
+ break if block.nil?
65
+ new_parent.blocks.push block
66
+ end
67
+
68
+ reader = Asciidoctor::Reader.new(subQuestAndAns)
69
+ answers_block = Asciidoctor::Parser.next_block reader, new_parent
70
+ new_parent.blocks.push answers_block
71
+ post_answers new_parent, tag
72
+
73
+ else
74
+ process_error_push new_parent, err, answers
75
+ end
76
+ new_parent
77
+
78
+
79
+ end
80
+
81
+ def prepare_answer_lines(lines)
82
+ lines
83
+ end
84
+
85
+ def prepare_answers(answers_block, tag)
86
+ answers_block.blocks.shuffle! if tag[:shuffle] == 'shuffle'
87
+ answers_block
88
+ end
89
+
90
+ end
91
+
92
+
93
+ class MOODLEMatchingBlockProcessor < MatchingBlockProcessor
94
+
95
+ def prepare_subQuest(subQuest, answers)
96
+ nbSub = subQuest.size
97
+ nbAns = answers.size
98
+ text="+++ "
99
+
100
+ preSubQuest = '<subquestion format="html">
101
+ <text><![CDATA[<p> '
102
+ postSubQuest = '</p>]]></text>
103
+ <answer>
104
+ <text>'
105
+ noSubQuest = "<subquestion format=\"html\">
106
+ <text/>
107
+ <answer>
108
+ <text>"
109
+ postAns = "</text>
110
+ </answer>
111
+ </subquestion>"
112
+
113
+ nbAns.times do |i|
114
+ if i<nbSub
115
+ text << preSubQuest << subQuest[i] << postSubQuest << answers[i] <<postAns
116
+ else
117
+ text << noSubQuest << answers[i] <<postAns
118
+ end
119
+ end
120
+ text << '</question> +++'
121
+ text
122
+ end
123
+
124
+ def prepare_question_lines(question, docName,id,nbSubQuest )
125
+
126
+ preQuest = "+++ <question type=\"matching\">
127
+ <name>
128
+ <text> #{docName}_question_#{id}_matching </text>
129
+ </name>
130
+ <questiontext format=\"html\">
131
+ <text><![CDATA[<p> +++"
132
+ postQuest = "+++ </p>]]></text>
133
+ </questiontext>
134
+ <generalfeedback format=\"html\">
135
+ <text></text>
136
+ </generalfeedback>
137
+ <defaultgrade>#{nbSubQuest}</defaultgrade>
138
+ <penalty>1</penalty>
139
+ <hidden>0</hidden>
140
+ <idnumber></idnumber>
141
+ <shuffleanswers>true</shuffleanswers>
142
+ <correctfeedback format=\"html\">
143
+ <text><![CDATA[<p>Your answer is correct.</p>]]></text>
144
+ </correctfeedback>
145
+ <partiallycorrectfeedback format=\"html\">
146
+ <text><![CDATA[<p>Your answer is partially correct.</p>]]></text>
147
+ </partiallycorrectfeedback>
148
+ <incorrectfeedback format=\"html\">
149
+ <text><![CDATA[<p>Your answer is incorrect.</p>]]></text>
150
+ </incorrectfeedback>
151
+ <shownumcorrect/> +++"
152
+
153
+ question.prepend(preQuest)
154
+ question.push(postQuest)
155
+
156
+ question
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,175 @@
1
+ require_relative '../extensions'
2
+
3
+ module Asciidoctor
4
+ module Question
5
+
6
+ class MultipleChoiceBlockProcessor < Extensions::BaseProcessor
7
+
8
+ def nbTrueF(lines)
9
+ nbTrue = 0
10
+ lines.map do |answer|
11
+ if answer =~ /^-\s?\[(X|\*)\]/
12
+ nbTrue +=1
13
+ end
14
+ end
15
+ nbTrue
16
+ end
17
+
18
+ def process(parent, source, tag)
19
+
20
+ docName = parent.attributes['docname']
21
+ id = tag[:id]
22
+ err = '+++ Error : there is no answer +++'
23
+ question = Array.new
24
+ answers = Array.new
25
+ switch = false
26
+ nbAnswer = 0
27
+
28
+ valShuffleNamed = tag["shuffle"]
29
+
30
+ valShuffle = tag[3]
31
+
32
+ if valShuffle == 'shuffle' || valShuffle == 'SHUFFLE' || valShuffleNamed == 'shuffle' || valShuffleNamed == 'SHUFFLE'
33
+ isShuffle = true
34
+ else
35
+ isShuffle = false
36
+ end
37
+
38
+ source.lines.each do |line|
39
+ switch = true if line =~ /^-\s?\[/
40
+ if switch
41
+ nbAnswer +=1
42
+ answers.push line
43
+ else
44
+ question.push line
45
+ end
46
+ end
47
+
48
+ answers, nbTrue = prepare_answer_lines answers
49
+
50
+ if nbTrue <= 1
51
+ question = prepare_question_lines(question, docName,id, isShuffle,true)
52
+ else
53
+ question = prepare_question_lines(question, docName,id, isShuffle,false)
54
+ end
55
+
56
+
57
+ new_parent = Asciidoctor::Block.new parent, :open, {:attributes => {'id' => "#{docName}_question_#{id}_mc"}}
58
+
59
+ if nbAnswer>0
60
+ reader = Asciidoctor::Reader.new(question)
61
+ loop do
62
+ block = Asciidoctor::Parser.next_block reader, new_parent
63
+ break if block.nil?
64
+ new_parent.blocks.push block
65
+ end
66
+
67
+ reader = Asciidoctor::Reader.new(answers)
68
+ answers_block = Asciidoctor::Parser.next_block reader, new_parent
69
+ new_parent.blocks.push answers_block
70
+ post_answers new_parent, tag
71
+
72
+ else
73
+
74
+ process_error_push new_parent, err, answers
75
+ end
76
+ new_parent
77
+
78
+
79
+ end
80
+
81
+ def prepare_answer_lines(lines)
82
+ lines
83
+ end
84
+
85
+ end
86
+
87
+ class MOODLEMultipleChoiceBlockProcessor < MultipleChoiceBlockProcessor
88
+
89
+ def nbTrueF(lines)
90
+ nbTrue = 0
91
+ lines.map do |answer|
92
+ if answer =~ /^-\s?\[(X|\*)\]/
93
+ nbTrue +=1
94
+ end
95
+ end
96
+ nbTrue
97
+ end
98
+
99
+ def prepare_answer_lines(lines)
100
+
101
+ nbTrue = nbTrueF(lines)
102
+ valFraction = (100.0 / nbTrue)
103
+ penality = -33.3333
104
+ noPenality = 0
105
+ if nbTrue >1
106
+ fract = penality
107
+ else
108
+ fract = noPenality
109
+ end
110
+ preAnsFaux = "<answer fraction=\"#{fract}\" format=\"html\">
111
+ <text>"
112
+ preAnsVrai = "<answer fraction=\"#{valFraction}\" format=\"html\">
113
+ <text>"
114
+ postAns = ' </text>
115
+ <feedback format="html">
116
+ <text></text>
117
+ </feedback>
118
+ </answer>'
119
+
120
+ lines.map! do |answer|
121
+ if answer =~ /^-\s?\[\s?\]/
122
+ answer.sub!(/^-\s?\[\s\]/, preAnsFaux)
123
+ answer << postAns
124
+ elsif answer =~ /^-\s?\[(X|\*)\]/
125
+ answer.sub!(/^-\s?\[(X|\*)\]/, preAnsVrai)
126
+ answer << postAns
127
+ else
128
+ answer
129
+ end
130
+ end
131
+
132
+ lines.prepend("+++")
133
+ lines.push('</question>')
134
+ lines.push('+++')
135
+ [lines, nbTrue]
136
+ end
137
+
138
+ def prepare_question_lines(lines, docName,id, isShuffle, is_single)
139
+
140
+ preQuest = "+++ <question type=\"multichoice\">
141
+ <name>
142
+ <text> #{docName}_question_#{id}_mc </text>
143
+ </name>
144
+ <questiontext format=\"html\">
145
+ <text><![CDATA[<p> +++"
146
+ postQuest = "+++ </p>]]></text>
147
+ </questiontext>
148
+ <generalfeedback format=\"html\">
149
+ <text></text>
150
+ </generalfeedback>
151
+ <defaultgrade>1</defaultgrade>
152
+ <penalty>0.3333333</penalty>
153
+ <hidden>0</hidden>
154
+ <idnumber></idnumber>
155
+ <single>#{is_single}</single>
156
+ <shuffleanswers>#{isShuffle}</shuffleanswers>
157
+ <answernumbering>abc</answernumbering>
158
+ <showstandardinstruction>1</showstandardinstruction>
159
+ <correctfeedback format=\"html\">
160
+ <text></text>
161
+ </correctfeedback>
162
+ <partiallycorrectfeedback format=\"html\">
163
+ <text></text>
164
+ </partiallycorrectfeedback>
165
+ <incorrectfeedback format=\"html\">
166
+ <text></text>
167
+ </incorrectfeedback> +++"
168
+
169
+ lines.prepend(preQuest)
170
+ lines.push(postQuest)
171
+ lines
172
+ end
173
+ end
174
+ end
175
+ end