Skip to content
This repository was archived by the owner on Jun 7, 2023. It is now read-only.

Commit 2378369

Browse files
committed
Test XML output of MChoice
* Convert to use the Node classes the "correct" way
1 parent bec1a0a commit 2378369

File tree

4 files changed

+111
-54
lines changed

4 files changed

+111
-54
lines changed

runestone/common/question_number.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
# ======
88
# Third-party
99
from docutils import nodes
10-
10+
import pdb
1111
# Local
1212
from .runestonedirective import RunestoneIdNode
1313
from ..server.componentdb import addQNumberToDB
@@ -18,8 +18,9 @@
1818
# After the section numbers are available from the TOC tree, determine the section number for each Runestone component with an ID.
1919
def _insert_qnum(app, doctree, docname):
2020
toc = app.env.toc_secnumbers.get(docname, {})
21-
21+
pdb.set_trace()
2222
# Return the section number tuple for the given ``section_ref`` if it exists, or None if not.
23+
2324
def get_secnum_tuple(section_ref):
2425
# The Sphinx TOC structure is::
2526
#
@@ -48,8 +49,9 @@ def get_secnum_tuple(section_ref):
4849
question_number_str = ".".join(map(str, question_number_tuple))
4950
# Update the database.
5051
addQNumberToDB(app, node, question_number_str)
51-
div_id = node.runestone_options["divid"]
52-
node.runestone_options["question_label"] = question_number_str
52+
pdb.set_trace()
53+
div_id = node["runestone_options"]["divid"]
54+
node["runestone_options"]["question_label"] = question_number_str
5355
# Prepare to number the next question.
5456
current_question_number += 1
5557
else:

runestone/mchoice/assess.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
from .multiplechoice import *
2222

2323

24-
2524
def setup(app):
2625
app.add_directive("mchoice", MChoice)
2726
app.add_directive("mchoicemf", MChoiceMF)
@@ -32,7 +31,9 @@ def setup(app):
3231

3332
app.add_config_value("mchoice_div_class", "runestone alert alert-warning", "html")
3433

35-
app.add_node(MChoiceNode, html=(visit_mc_node, depart_mc_node))
34+
app.add_node(MChoiceNode, html=(visit_mc_node, depart_mc_node),
35+
xml=(visit_mc_xml, depart_mc_xml))
36+
3637
app.add_node(
3738
AnswersBulletList, html=(visit_answers_bullet_node, depart_answers_bullet_node)
3839
)

runestone/mchoice/multiplechoice.py

Lines changed: 101 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# You should have received a copy of the GNU General Public License
1414
# along with this program. If not, see <http://www.gnu.org/licenses/>.
1515
#
16+
import pdb
1617
__author__ = "bmiller"
1718

1819
from docutils import nodes
@@ -31,35 +32,85 @@
3132

3233

3334
class MChoiceNode(nodes.General, nodes.Element, RunestoneIdNode):
34-
def __init__(self, content, **kwargs):
35-
"""
35+
pass
36+
# def __init__(self, content, **kwargs):
37+
# """
38+
39+
# Arguments:
40+
# - `self`:
41+
# - `content`:
42+
# """
43+
44+
# super(MChoiceNode, self).__init__(**kwargs)
45+
# pdb.set_trace()
46+
# if type(content) == str:
47+
# self.runestone_options = self.attributes['opts']
48+
# else:
49+
# self.runestone_options = content
50+
51+
52+
def visit_mc_xml(self, node):
53+
54+
res = ""
55+
if "random" in node["runestone_options"]:
56+
node["runestone_options"]["random"] = "data-random"
57+
else:
58+
node["runestone_options"]["random"] = ""
59+
# Use multiple_answers behavior if explicitly required or if multiple correct answers were provided.
60+
if ("multiple_answers" in node["runestone_options"]) or (
61+
"," in node["runestone_options"]["correct"]
62+
):
63+
node["runestone_options"]["multipleAnswers"] = "true"
64+
else:
65+
node["runestone_options"]["multipleAnswers"] = "false"
66+
res = node["template_start"] % node["runestone_options"]
67+
68+
self.output.append(res)
3669

37-
Arguments:
38-
- `self`:
39-
- `content`:
40-
"""
41-
super(MChoiceNode, self).__init__(**kwargs)
42-
self.runestone_options = content
70+
71+
def depart_mc_xml(self, node):
72+
res = ""
73+
currFeedback = ""
74+
# Add all of the possible answers
75+
okeys = list(node["runestone_options"].keys())
76+
okeys.sort()
77+
for k in okeys:
78+
if "answer_" in k:
79+
x, label = k.split("_")
80+
node["runestone_options"]["alabel"] = label
81+
node["runestone_options"]["atext"] = node["runestone_options"][k]
82+
currFeedback = "feedback_" + label
83+
node["runestone_options"]["feedtext"] = node["runestone_options"].get(
84+
currFeedback, ""
85+
) # node["runestone_options"][currFeedback]
86+
if label in node["runestone_options"]["correct"]:
87+
node["runestone_options"]["is_correct"] = "data-correct"
88+
else:
89+
node["runestone_options"]["is_correct"] = ""
90+
res += node["template_option"] % node["runestone_options"]
91+
92+
res += node["template_end"] % node["runestone_options"]
93+
self.output.append(res)
4394

4495

4596
def visit_mc_node(self, node):
4697

47-
node.delimiter = "_start__{}_".format(node.runestone_options["divid"])
48-
self.body.append(node.delimiter)
98+
node["delimiter"] = "_start__{}_".format(node["runestone_options"]["divid"])
99+
self.body.append(node["delimiter"])
49100

50101
res = ""
51-
if "random" in node.runestone_options:
52-
node.runestone_options["random"] = "data-random"
102+
if "random" in node["runestone_options"]:
103+
node["runestone_options"]["random"] = "data-random"
53104
else:
54-
node.runestone_options["random"] = ""
105+
node["runestone_options"]["random"] = ""
55106
# Use multiple_answers behavior if explicitly required or if multiple correct answers were provided.
56-
if ("multiple_answers" in node.runestone_options) or (
57-
"," in node.runestone_options["correct"]
107+
if ("multiple_answers" in node["runestone_options"]) or (
108+
"," in node["runestone_options"]["correct"]
58109
):
59-
node.runestone_options["multipleAnswers"] = "true"
110+
node["runestone_options"]["multipleAnswers"] = "true"
60111
else:
61-
node.runestone_options["multipleAnswers"] = "false"
62-
res = node.template_start % node.runestone_options
112+
node["runestone_options"]["multipleAnswers"] = "false"
113+
res = node["template_start"] % node["runestone_options"]
63114

64115
self.body.append(res)
65116

@@ -68,33 +119,33 @@ def depart_mc_node(self, node):
68119
res = ""
69120
currFeedback = ""
70121
# Add all of the possible answers
71-
okeys = list(node.runestone_options.keys())
122+
okeys = list(node["runestone_options"].keys())
72123
okeys.sort()
73124
for k in okeys:
74125
if "answer_" in k:
75126
x, label = k.split("_")
76-
node.runestone_options["alabel"] = label
77-
node.runestone_options["atext"] = node.runestone_options[k]
127+
node["runestone_options"]["alabel"] = label
128+
node["runestone_options"]["atext"] = node["runestone_options"][k]
78129
currFeedback = "feedback_" + label
79-
node.runestone_options["feedtext"] = node.runestone_options.get(
130+
node["runestone_options"]["feedtext"] = node["runestone_options"].get(
80131
currFeedback, ""
81-
) # node.runestone_options[currFeedback]
82-
if label in node.runestone_options["correct"]:
83-
node.runestone_options["is_correct"] = "data-correct"
132+
) # node["runestone_options"][currFeedback]
133+
if label in node["runestone_options"]["correct"]:
134+
node["runestone_options"]["is_correct"] = "data-correct"
84135
else:
85-
node.runestone_options["is_correct"] = ""
86-
res += node.template_option % node.runestone_options
136+
node["runestone_options"]["is_correct"] = ""
137+
res += node["template_option"] % node["runestone_options"]
87138

88-
res += node.template_end % node.runestone_options
139+
res += node["template_end"] % node["runestone_options"]
89140
self.body.append(res)
90141

91142
addHTMLToDB(
92-
node.runestone_options["divid"],
93-
node.runestone_options["basecourse"],
94-
"".join(self.body[self.body.index(node.delimiter) + 1 :]),
143+
node["runestone_options"]["divid"],
144+
node["runestone_options"]["basecourse"],
145+
"".join(self.body[self.body.index(node["delimiter"]) + 1 :]),
95146
)
96147

97-
self.body.remove(node.delimiter)
148+
self.body.remove(node["delimiter"])
98149

99150

100151
#####################
@@ -210,12 +261,15 @@ def run(self):
210261
"""
211262
addQuestionToDB(self)
212263

213-
mcNode = MChoiceNode(self.options, rawsource=self.block_text)
214-
mcNode.source, mcNode.line = self.state_machine.get_source_and_line(self.lineno)
215-
mcNode.template_start = TEMPLATE_START
216-
mcNode.template_option = OPTION
217-
mcNode.template_end = TEMPLATE_END
218-
264+
mcNode = MChoiceNode()
265+
# This is maybe a hack or maybe the new right way to do this...
266+
mcNode["runestone_options"] = self.options
267+
mcNode["source"], mcNode["line"] = self.state_machine.get_source_and_line(
268+
self.lineno)
269+
mcNode["template_start"] = TEMPLATE_START
270+
mcNode["template_option"] = OPTION
271+
mcNode["template_end"] = TEMPLATE_END
272+
pdb.set_trace()
219273
# For MChoice its better to insert the qnum into the content before further processing.
220274
self.updateContent()
221275

@@ -258,9 +312,9 @@ def run(self):
258312
feedback_bullet_list = answer_list_item[-1]
259313
if (
260314
not isinstance(feedback_bullet_list, nodes.bullet_list)
261-
or
315+
262316
# It should have just one item (the feedback itself).
263-
(len(feedback_bullet_list) != 1)
317+
or (len(feedback_bullet_list) != 1)
264318
):
265319
raise self.error(
266320
"On line {}, a single-item list must be nested under each answer.".format(
@@ -363,16 +417,16 @@ def visit_answer_list_item(self, node):
363417
# _`label`: Turn the index of this item in the answer_bullet_list (see structure_) into a letter.
364418
label = chr(node.parent.index(node) + ord("a"))
365419
# Update dict for formatting the HTML.
366-
mcNode.runestone_options["alabel"] = label
367-
if label in mcNode.runestone_options["correct"]:
368-
mcNode.runestone_options["is_correct"] = "data-correct"
420+
mcnode["runestone_options"]["alabel"] = label
421+
if label in mcnode["runestone_options"]["correct"]:
422+
mcnode["runestone_options"]["is_correct"] = "data-correct"
369423
else:
370-
mcNode.runestone_options["is_correct"] = ""
424+
mcnode["runestone_options"]["is_correct"] = ""
371425

372426
# Format the HTML.
373427
self.body.append(
374428
'<li data-component="answer" %(is_correct)s id="%(divid)s_opt_%(alabel)s">'
375-
% mcNode.runestone_options
429+
% mcnode["runestone_options"]
376430
)
377431

378432

@@ -396,10 +450,10 @@ def visit_feedback_list_item(self, node):
396450
answer_list_item = node.parent.parent
397451
mcNode = answer_list_item.parent.parent
398452
label = chr(answer_list_item.parent.index(answer_list_item) + ord("a"))
399-
mcNode.runestone_options["alabel"] = label
453+
mcnode["runestone_options"]["alabel"] = label
400454
self.body.append(
401455
'</li><li data-component="feedback" id="%(divid)s_opt_%(alabel)s">\n'
402-
% mcNode.runestone_options
456+
% mcnode["runestone_options"]
403457
)
404458

405459

runestone/server/componentdb.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ def addQNumberToDB(app, node, qnumber):
420420
questions.update()
421421
.where(
422422
and_(
423-
questions.c.name == node.runestone_options["divid"],
423+
questions.c.name == node["runestone_options"]["divid"],
424424
questions.c.base_course == basecourse,
425425
)
426426
)

0 commit comments

Comments
 (0)