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

Commit 9777048

Browse files
committed
✨ Specifying arguments for hparsons
1 parent e4a3e7c commit 9777048

4 files changed

Lines changed: 234 additions & 85 deletions

File tree

runestone/hparsons/hparsons.py

Lines changed: 115 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
#
1616
__author__ = "ziwu"
1717

18+
from cgi import test
19+
import json
1820
from docutils import nodes
1921
from docutils.parsers.rst import directives
2022
from runestone.mchoice import Assessment
@@ -39,14 +41,16 @@ def setup(app):
3941

4042
# TODO: what is the alert and alert-warnings?
4143
TEMPLATE_START = """
42-
<div style="max-width: none;">
44+
<div>
4345
<div data-component="hparsons" id="%(divid)s" data-question_label="%(question_label)s" class="alert alert-warning hparsons" >
44-
<div class="hparsons_question hparsons-text" >
46+
<div>
4547
"""
4648

4749
TEMPLATE_END = """
4850
</div>
49-
<div class="hparsons">
51+
<div class="hparsons_question hparsons-text" >%(problem)s</div>
52+
<div class="hparsons %(hidetests)s" %(textentry)s %(nostrictmatch)s>
53+
<pre>%(settings)s</pre>
5054
</div>
5155
</div>
5256
</div>
@@ -86,32 +90,131 @@ def depart_hparsons_node(self, node):
8690
class HParsonsDirective(Assessment):
8791
"""
8892
.. hparsons:: uniqueid
89-
90-
nothing makes sense at all.
91-
93+
:textentry: if you will use text entry instead of horizontal parsons
94+
:hidetests: if the unittests will be hidden from learners
95+
:nostrictmatch: if the answer is required to match the whole string. if not selected, the tool will add ^ and $ automatically to the answer to force matching the full string. This does not affect the test string area.
96+
97+
--problem--
98+
Here is the problem description.
99+
Make sure you use the correct delimitier for each section.
100+
--blocks--
101+
block 1
102+
block 2
103+
--explanations--
104+
explanations for block 1
105+
explanations for block 2
106+
--positive test string--
107+
this is some positive test string.
108+
it can be more than one line.
109+
just ignore this section if you do not want to put anything in there.
110+
--negative test string--
111+
this is some negative test string.
112+
--test cases--
113+
input string 1
114+
['expected match 1', 'expected match 2']
115+
input string 2
116+
[]
117+
input string 3
118+
[]
92119
"""
93120

94121
required_arguments = 1 # the div id
95-
optional_arguments = 0
96-
final_argument_whitespace = True
122+
optional_arguments = 1
123+
final_argument_whitespace = False
97124
has_content = True
98125
option_spec = Assessment.option_spec.copy()
126+
option_spec.update(
127+
{
128+
"textentry": directives.flag,
129+
"hidetests": directives.flag,
130+
"nostrictmatch": directives.flag,
131+
}
132+
)
99133
# seem to be defining the type of the options
100134
# option_spec.update({"mathjax": directives.flag})
101135

102136
# just fill it with the name
103-
node_class = HParsonsNode
137+
# node_class = HParsonsNode
104138

105139
def run(self):
106140
# same
107141
super(HParsonsDirective, self).run()
108142
addQuestionToDB(self)
109143
# Raise an error if the directive does not have contents.
144+
# env = self.state.document.settings.env
145+
if "textentry" in self.options:
146+
self.options['textentry'] = ' data-textentry="true"'
147+
else:
148+
self.options['textentry'] = ''
149+
if "hidetests" in self.options:
150+
self.options['hidetests'] = 'hidetests'
151+
else:
152+
self.options['hidetests'] = ''
153+
if "nostrictmatch" in self.options:
154+
self.options['nostrictmatch'] = ' data-nostrictmatch="true"'
155+
else:
156+
self.options['nostrictmatch'] = ''
157+
110158
self.assert_has_content()
111159

112-
# specifying default for option?
113-
# TODO: ignoring for now
114-
# self.options["mathjax"] = "data-mathjax" if "mathjax" in self.options else ""
160+
# sepcifying the start end end for each section
161+
delimitiers = ['--problem--', '--blocks--', '--explanations--', '--positive test string--', '--negative test string--', '--test cases--']
162+
delimitiers_index = [-1 for x in range(6)]
163+
164+
has_content = False
165+
for i in range(len(delimitiers)):
166+
if delimitiers[i] in self.content:
167+
has_content = True
168+
delimitiers_index[i] = self.content.index(delimitiers[i])
169+
if has_content:
170+
sorted_index, sorted_delimiters = [list(t) for t in zip(*[pair for pair in sorted(zip(delimitiers_index, delimitiers)) if pair[0] > 0])]
171+
else:
172+
sorted_index = []
173+
sorted_delimiters = []
174+
175+
content = self.content
176+
177+
parsons_settings = {}
178+
179+
if '--problem--' in sorted_delimiters:
180+
index = sorted_delimiters.index('--problem--')
181+
self.options['problem'] = '\n'.join(content[(sorted_index[index] + 1): (sorted_index[index + 1] if index + 1 < len(sorted_index) else len(content))])
182+
else:
183+
self.options['problem'] = 'empty problem'
184+
185+
if '--blocks--' in sorted_delimiters:
186+
index = sorted_delimiters.index('--blocks--')
187+
parsons_settings['blocks'] = list(content[sorted_index[index] + 1: (sorted_index[index + 1] if index + 1 < len(sorted_index) else len(content))])
188+
else:
189+
parsons_settings['blocks'] = []
190+
191+
if '--explanations--' in sorted_delimiters:
192+
index = sorted_delimiters.index('--explanations--')
193+
parsons_settings['explanations'] = list(content[sorted_index[index] + 1: (sorted_index[index + 1] if index + 1 < len(sorted_index) else len(content))])
194+
195+
if '--positive test string--' in sorted_delimiters:
196+
index = sorted_delimiters.index('--positive test string--')
197+
parsons_settings['positivetest'] = '\n'.join(content[sorted_index[index] + 1: (sorted_index[index + 1] if index + 1 < len(sorted_index) else len(content))])
198+
199+
if '--negative test string--' in sorted_delimiters:
200+
index = sorted_delimiters.index('--negative test string--')
201+
parsons_settings['negativetest'] = '\n'.join(content[sorted_index[index] + 1: (sorted_index[index + 1] if index + 1 < len(sorted_index) else len(content))])
202+
203+
if '--test cases--' in sorted_delimiters:
204+
index = sorted_index[sorted_delimiters.index('--test cases--')] + 1
205+
end_index = sorted_index[index + 1] if index + 1 < len(sorted_index) else len(content)
206+
parsons_settings['testcases'] = []
207+
while index < end_index:
208+
testcase = {}
209+
testcase['input'] = content[index]
210+
testcase['expect'] = content[index + 1] if index + 1 < end_index else []
211+
parsons_settings['testcases'].append(testcase)
212+
index += 2
213+
214+
self.options['settings'] = json.dumps(parsons_settings)
215+
216+
# same
217+
maybeAddToAssignment(self)
115218

116219
# same
117220
hparsons_node = HParsonsNode(self.options, rawsource=self.block_text)
@@ -124,21 +227,11 @@ def run(self):
124227
self.updateContent()
125228

126229
# same as mchoice, different from parsons. i think it is for generating instructions.
127-
self.state.nested_parse(self.content, self.content_offset, hparsons_node)
128230
# parsons:
129231
# self.state.nested_parse(
130232
# self.options["instructions"], self.content_offset, parsons_node
131233
# )
132234

133-
# adding classes outside of the div based on the options
134-
env = self.state.document.settings.env
135-
if self.options["optional"]:
136-
self.options["divclass"] = env.config.shortanswer_optional_div_class
137-
else:
138-
self.options["divclass"] = env.config.shortanswer_div_class
139-
140-
# same
141-
maybeAddToAssignment(self)
142235

143236
# same
144237
return [hparsons_node]

runestone/hparsons/js/hparsons.js

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,6 @@ export default class HParsons extends RunestoneBase {
2727
var orig = opts.orig; // entire <pre> element that will be replaced by new HTML
2828
this.containerDiv = orig;
2929
this.origElem = $(orig).find(".hparsons")[0];
30-
console.log('hparsons')
31-
console.log(orig)
3230
console.log(this.origElem)
3331
this.useRunestoneServices =
3432
opts.useRunestoneServices || eBookConfig.useRunestoneServices;
@@ -54,12 +52,50 @@ export default class HParsons extends RunestoneBase {
5452
}
5553

5654
renderHTML() {
57-
console.log('renderhtml');
5855
// const div = document.createElement('regex-element');
5956
// div.setAttribute('input-type', 'parsons')
6057
// div.id = 'abcd';
6158
// console.log(this.origElem)
62-
$(this.origElem).html('<regex-element id="tool-area"></regex-element>');
59+
let attributes = '';
60+
console.log($(this.origElem).data("textentry"))
61+
let settings = JSON.parse($(this.origElem).children()[0].innerText)
62+
attributes += ' input-type=' + ($(this.origElem).data("textentry") ? 'text' : 'parsons' );
63+
$(this.origElem).html('<regex-element' + attributes + '></regex-element>');
64+
let regexElement = $(this.origElem).children()[0];
65+
if ($(this.origElem).data("nostrictmatch")) {
66+
regexElement.unitTestTable.strictMatch = false;
67+
} else {
68+
regexElement.unitTestTable.strictMatch = true;
69+
}
70+
if ($(this.origElem).data("hidetests")) {
71+
regexElement.hidetests = false;
72+
} else {
73+
regexElement.unitTestTable.strictMatch = true;
74+
}
75+
76+
if (settings.blocks) {
77+
regexElement.parsonsData = settings.blocks;
78+
}
79+
if (settings.explanations) {
80+
regexElement.parsonsExplanation = settings.explanations;
81+
}
82+
if (settings.positivetest) {
83+
regexElement.setPositiveInitialTestString(settings.positivetest);
84+
}
85+
if (settings.negativetest) {
86+
regexElement.setNegativeInitialTestString(settings.negativetest);
87+
}
88+
if (settings.testcases) {
89+
regexElement.setTestCases(settings.testcases);
90+
}
91+
92+
// tool.parsonsExplanation = toolConfig.parsonsExplanation;
93+
// tool.parsonsData = toolConfig.parsonsData;
94+
regexElement.resetTool();
95+
// tool.setTestCases(toolConfig.testCases);
96+
// tool.setPositiveInitialTestString(toolConfig.positiveInitialTestString);
97+
// tool.setNegativeInitialTestString(toolConfig.negativeInitialTestString);
98+
6399
// console.log(this.origElem)
64100
// $(this.origElem).replaceWith(document.createElement('regex-element'));
65101
// $(this.elem).innerHTML = `<regex-element input-type='parsons' id="abcd"></regex-element>`;
@@ -213,7 +249,6 @@ $(document).bind("runestone:login-complete", function () {
213249
$("[data-component=hparsons]").each(function () {
214250
if ($(this).closest("[data-component=timedAssessment]").length == 0) {
215251
// If this element exists within a timed component, don't render it here
216-
console.log('rendering hparsons')
217252
// try {
218253
hpList[this.id] = new HParsons({
219254
orig: this,

runestone/hparsons/js/regex-element.js

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16232,9 +16232,9 @@ class RegexElement extends HTMLElement {
1623216232
let match;
1623316233
while ((match = regex.exec(str)) != null) {
1623416234
// console.log(match)
16235-
console.log("Matched '" + match[0] + "' at position " + match.index +
16236-
"; next search at " + regex.lastIndex);
16237-
console.log("match: " + JSON.stringify(match));
16235+
// console.log("Matched '" + match[0] + "' at position " + match.index +
16236+
// "; next search at " + regex.lastIndex);
16237+
// console.log("match: " + JSON.stringify(match));
1623816238
pos_result.push({ st: match.index, ed: regex.lastIndex });
1623916239
if (match.length < 2) {
1624016240
match_result.push(new window.Sk.builtin.str(match[0]));
@@ -16259,7 +16259,7 @@ class RegexElement extends HTMLElement {
1625916259
constructor() {
1626016260
super();
1626116261
RegexElement.toolCount += 1;
16262-
console.log(RegexElement.toolCount);
16262+
// console.log(RegexElement.toolCount);
1626316263
this.toolNumber = RegexElement.toolCount;
1626416264
this.root = this.attachShadow({ mode: 'open' });
1626516265
window.Sk.configure({
@@ -16413,15 +16413,15 @@ class RegexElement extends HTMLElement {
1641316413
enabled: false
1641416414
};
1641516415
this.logEvent(visibilityStatusEvent);
16416-
console.log("visibility not working");
16416+
// console.log("visibility not working");
1641716417
}
1641816418
else {
1641916419
const visibilityStatusEvent = {
1642016420
'event-type': 'page-visibility-status',
1642116421
enabled: true
1642216422
};
1642316423
this.logEvent(visibilityStatusEvent);
16424-
console.log("add visibility change");
16424+
// console.log("add visibility change");
1642516425
document.addEventListener("visibilitychange", (event) => {
1642616426
let pageStatusEvent;
1642716427
if (document.hidden) {
@@ -16461,8 +16461,8 @@ class RegexElement extends HTMLElement {
1646116461
// stub for student and problem id
1646216462
this.studentId = this._getStudentIdFromURL();
1646316463
this.problemId = this.getAttribute('problem-id') || '';
16464-
console.log(this.studentId);
16465-
console.log(this.problemId);
16464+
// console.log(this.studentId);
16465+
// console.log(this.problemId);
1646616466
this.temporaryInputEvent = null;
1646716467
this.matchFindall = true;
1646816468
}
@@ -16522,7 +16522,7 @@ class RegexElement extends HTMLElement {
1652216522
// sheet.innerHTML += '.regex-unittest > table, .regex-unittest td {border: 1px solid black; padding: 3px; text-align: center;}\n'
1652316523
// sheet.innerHTML += '.regex-unittest.collapse{display:none;}\n'
1652416524
// for study 0: hide the table
16525-
// sheet.innerHTML += '.regex-unittest{display:none;}\n'
16525+
sheet.innerHTML += '.hidetests .regex-unittest{display:none;}\n';
1652616526
document.body.appendChild(sheet);
1652716527
this.root.appendChild(sheet);
1652816528
const global_sheet = document.createElement('style');

0 commit comments

Comments
 (0)