1515#
1616__author__ = "ziwu"
1717
18+ from cgi import test
19+ import json
1820from docutils import nodes
1921from docutils .parsers .rst import directives
2022from runestone .mchoice import Assessment
@@ -39,14 +41,16 @@ def setup(app):
3941
4042# TODO: what is the alert and alert-warnings?
4143TEMPLATE_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
4749TEMPLATE_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):
8690class 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 ]
0 commit comments