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

Commit 154b393

Browse files
committed
readying the js Parson's implementation for PR
1 parent 12c00a0 commit 154b393

File tree

8 files changed

+1369
-1139
lines changed

8 files changed

+1369
-1139
lines changed

runestone/parsons/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#Parsons problem
2+
3+
The Parson's problem allows for giving students simple programming problems where the code is already there but not indented or in the correct order. They drag-and-drop lines of code into the proper order.
4+
5+
Each line of code is delimited by `---` and the indentation of the line is the same as the indentation of that line in the markup.
6+
7+
```html
8+
<pre data-component="parsons" id="example1">
9+
<span data-question>This is the question</span>
10+
x = 0
11+
---
12+
for i in range(10)
13+
---
14+
x = x + 1
15+
---
16+
print(x)
17+
</pre>
18+
```
19+
20+

runestone/parsons/__init__.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
1-
21
from .parsons import *
3-

runestone/parsons/bootstrap.min.css

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

runestone/parsons/css/parsons.css

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,13 @@ li.correctPosition, .testcase.pass {
7272
.testcase .msg { font-weight: bold; }
7373
.testcase .error { color: red;}
7474
.testcase .feedback { font-weight: bolder;}
75-
.testcase.fail .expected, .testcase.fail .actual {
76-
color: red;
75+
.testcase.fail .expected, .testcase.fail .actual {
76+
color: red;
7777
font-weight: bolder;
7878
}
79-
.testcase .output {
80-
display: block;
81-
white-space: pre;
79+
.testcase .output {
80+
display: block;
81+
white-space: pre;
8282
background-color: #555555;
8383
color: white;
8484
font-size: 12px;

runestone/parsons/js/parsons.js

Lines changed: 256 additions & 960 deletions
Large diffs are not rendered by default.

runestone/parsons/js/parsonsaux.js

Lines changed: 971 additions & 0 deletions
Large diffs are not rendered by default.

runestone/parsons/parsons.py

Lines changed: 45 additions & 172 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +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-
__author__ = 'hewner'
16+
__author__ = 'isaiahmayerchak'
1717

1818
import re
1919
from docutils import nodes
@@ -28,17 +28,44 @@ def setup(app):
2828
app.add_stylesheet('parsons.css')
2929
app.add_stylesheet('lib/prettify.css')
3030

31+
app.add_node(ParsonsNode, html=(visit_prs_node, depart_prs_node))
32+
3133
# includes parsons specific javascript headers
3234
# parsons-noconflict reverts underscore and
3335
# jquery to their original versions
34-
app.add_javascript('lib/jquery.min.js')
35-
app.add_javascript('lib/jquery-ui.min.js')
3636
app.add_javascript('lib/prettify.js')
3737
app.add_javascript('lib/underscore-min.js')
3838
app.add_javascript('lib/lis.js')
3939
app.add_javascript('parsons.js')
40-
app.add_javascript('parsons-noconflict.js')
40+
app.add_javascript('parsonsaux.js')
41+
42+
43+
TEMPLATE = '''
44+
<pre data-component="parsons" id="%(divid)s">
45+
<span data-question>%(qnumber)s: %(instructions)s</span>%(code)s</pre>
46+
'''
4147

48+
class ParsonsNode(nodes.General, nodes.Element):
49+
def __init__(self,content):
50+
"""
51+
Arguments:
52+
- `self`:
53+
- `content`:
54+
"""
55+
super(ParsonsNode,self).__init__()
56+
self.prs_options = content
57+
58+
def visit_prs_node(self,node):
59+
res = TEMPLATE % node.prs_options
60+
61+
self.body.append(res)
62+
63+
def depart_prs_node(self,node):
64+
''' This is called at the start of processing an datafile node. If parsons had recursive nodes
65+
etc and did not want to do all of the processing in visit_prs_node any finishing touches could be
66+
added here.
67+
'''
68+
pass
4269

4370
class ParsonsProblem(Assessment):
4471
required_arguments = 1
@@ -49,18 +76,13 @@ class ParsonsProblem(Assessment):
4976

5077
def run(self):
5178
"""
52-
53-
Instructions for solving the problem should be written and then a line with -----
79+
Instructions for solving the problem should be written and then a line with -----
5480
signals the beginning of the code. If you want more than one line in a single
5581
code block, seperate your code blocks with =====.
56-
5782
Both the instructions sections and code blocks are optional. If you don't include any
5883
=====, the code will assume you want each line to be its own code block.
59-
6084
Example:
61-
6285
.. parsonsprob:: unqiue_problem_id_here
63-
6486
Solve my really cool parsons problem...if you can.
6587
-----
6688
def findmax(alist):
@@ -76,173 +98,24 @@ def findmax(alist):
7698
curmax = item
7799
=====
78100
return curmax
79-
80-
81101
"""
82-
83-
template_values = {}
84-
template_values['qnumber'] = self.getNumber()
85-
template_values['unique_id'] = self.lineno
86-
template_values['instructions'] = ""
87-
code = self.content
102+
self.options['divid'] = self.arguments[0]
103+
self.options['qnumber'] = self.getNumber()
104+
self.options['instructions'] = ""
105+
self.options['code'] = self.content
88106

89107
if '-----' in self.content:
90108
index = self.content.index('-----')
91-
template_values['instructions'] = "\n".join(self.content[:index])
92-
code = self.content[index + 1:]
109+
self.options['instructions'] = "\n".join(self.content[:index])
110+
self.options['code'] = self.content[index + 1:]
93111

94-
if '=====' in code:
95-
template_values['code'] = self.parse_multiline_parsons(code);
112+
if '=====' in self.options['code']:
113+
self.options['code'] = self.options['code'].replace('====', '---')
96114
else:
97-
template_values['code'] = "\n".join(code)
98-
99-
template_values['divid'] = self.arguments[0]
100-
101-
TEMPLATE = '''
102-
<div class='parsons alert alert-warning' id="parsons-%(unique_id)s">
103-
<div class="parsons-text">%(qnumber)s: %(instructions)s<br /><br /></div>
104-
<div style="clear:left;"></div>
105-
<div id="parsons-orig-%(unique_id)s" style="display:none;">%(code)s</div>
106-
<div class="sortable-code-container">
107-
<div id="parsons-sortableTrash-%(unique_id)s" class="sortable-code"></div>
108-
<div id="parsons-sortableCode-%(unique_id)s" class="sortable-code"></div>
109-
<div style="clear:left;"></div>
110-
</div>
111-
<div class="parsons-controls">
112-
<input type="button" class='btn btn-success' id="checkMe%(unique_id)s" value="Check Me"/>
113-
<input type="button" class='btn btn-default' id="reset%(unique_id)s" value="Reset"/>
114-
<div id="parsons-message-%(unique_id)s"></div>
115-
</div>
116-
</div>
117-
118-
<script>
119-
$pjQ(document).ready(function(){
120-
121-
$("#parsons-%(unique_id)s").not(".sortable-code").not(".parsons-controls").on("click", function(){
122-
$('html, body').animate({
123-
scrollTop: ($("#parsons-%(unique_id)s").offset().top - 50)
124-
}, 700);
125-
}).find(".sortable-code, .parsons-controls").click(function(e) {
126-
return false;
127-
});
128-
var msgBox = $("#parsons-message-%(unique_id)s");
129-
msgBox.hide();
130-
var displayErrors = function (fb) {
131-
if(fb.errors.length > 0) {
132-
var hash = pp_%(unique_id)s.getHash("#ul-parsons-sortableCode-%(unique_id)s");
133-
msgBox.fadeIn(500);
134-
msgBox.attr('class','alert alert-danger');
135-
msgBox.html(fb.errors[0]);
136-
logBookEvent({'event':'parsons', 'act':hash, 'div_id':'%(divid)s'});
137-
138-
} else {
139-
logBookEvent({'event':'parsons', 'act':'yes', 'div_id':'%(divid)s'});
140-
msgBox.fadeIn(100);
141-
msgBox.attr('class','alert alert-success');
142-
msgBox.html("Perfect!")
143-
}
144-
145-
}
146-
147-
$(window).load(function() {
148-
// set min width and height
149-
var sortableul = $("#ul-parsons-sortableCode-%(unique_id)s");
150-
var trashul = $("#ul-parsons-sortableTrash-%(unique_id)s");
151-
var sortableHeight = sortableul.height();
152-
var sortableWidth = sortableul.width();
153-
var trashWidth = trashul.width();
154-
var trashHeight = trashul.height();
155-
var minHeight = Math.max(trashHeight,sortableHeight);
156-
var minWidth = Math.max(trashWidth, sortableWidth);
157-
trashul.css("min-height",minHeight + "px");
158-
sortableul.css("min-height",minHeight + "px");
159-
sortableul.height(minHeight);
160-
trashul.css("min-width",minWidth + "px");
161-
sortableul.css("min-width",minWidth + "px");
162-
});
163-
164-
165-
var pp_%(unique_id)s = new ParsonsWidget({
166-
'sortableId': 'parsons-sortableCode-%(unique_id)s',
167-
'trashId': 'parsons-sortableTrash-%(unique_id)s',
168-
'max_wrong_lines': 1,
169-
'solution_label': 'Drop blocks here',
170-
'feedback_cb' : displayErrors
171-
});
172-
pp_%(unique_id)s.init($pjQ("#parsons-orig-%(unique_id)s").text());
173-
pp_%(unique_id)s.shuffleLines();
174-
175-
if(localStorage.getItem('%(divid)s') && localStorage.getItem('%(divid)s-trash')) {
176-
try {
177-
var solution = localStorage.getItem('%(divid)s');
178-
var trash = localStorage.getItem('%(divid)s-trash');
179-
pp_%(unique_id)s.createHTMLFromHashes(solution,trash);
180-
pp_%(unique_id)s.getFeedback();
181-
} catch(err) {
182-
var text = "An error occured restoring old %(divid)s state. Error: ";
183-
console.log(text + err.message);
184-
}
185-
186-
}
187-
$pjQ("#reset%(unique_id)s").click(function(event){
188-
event.preventDefault();
189-
pp_%(unique_id)s.shuffleLines();
190-
191-
// set min width and height
192-
var sortableul = $("#ul-parsons-sortableCode-%(unique_id)s");
193-
var trashul = $("#ul-parsons-sortableTrash-%(unique_id)s");
194-
var sortableHeight = sortableul.height();
195-
var sortableWidth = sortableul.width();
196-
var trashWidth = trashul.width();
197-
var trashHeight = trashul.height();
198-
var minHeight = Math.max(trashHeight,sortableHeight);
199-
var minWidth = Math.max(trashWidth, sortableWidth);
200-
trashul.css("min-height",minHeight + "px");
201-
sortableul.css("min-height",minHeight + "px");
202-
trashul.css("min-width",minWidth + "px");
203-
sortableul.css("min-width",minWidth + "px");
204-
msgBox.hide();
205-
});
206-
207-
$pjQ("#checkMe%(unique_id)s").click(function(event){
208-
event.preventDefault();
209-
var hash = pp_%(unique_id)s.getHash("#ul-parsons-sortableCode-%(unique_id)s");
210-
localStorage.setItem('%(divid)s',hash);
211-
hash = pp_%(unique_id)s.getHash("#ul-parsons-sortableTrash-%(unique_id)s");
212-
localStorage.setItem('%(divid)s-trash',hash);
213-
214-
pp_%(unique_id)s.getFeedback();
215-
msgBox.fadeIn(100);
216-
217-
});
218-
219-
});
220-
221-
222-
</script>
223-
224-
'''
115+
self.options['code'] = "\n".join(self.options['code'])
116+
117+
self.options['divid'] = self.arguments[0]
118+
print(self.options)
225119

226120
self.assert_has_content()
227-
return [nodes.raw('', TEMPLATE % template_values, format='html')]
228-
229-
def parse_multiline_parsons(self, lines):
230-
current_block = []
231-
results = []
232-
for line in lines:
233-
if (line == '====='):
234-
results.append(self.convert_leading_whitespace_for_block(current_block))
235-
current_block = []
236-
else:
237-
current_block.append(line)
238-
results.append(self.convert_leading_whitespace_for_block(current_block))
239-
return "\n".join(results)
240-
241-
def convert_leading_whitespace_for_block(self, block):
242-
whitespaceMatcher = re.compile("^\s*")
243-
initialWhitespace = whitespaceMatcher.match(block[0]).end()
244-
result = block[0]
245-
for line in block[1:]:
246-
result += '\\n' # not a line break...the literal characters \n
247-
result += line[initialWhitespace:]
248-
return result
121+
return [ParsonsNode(self.options)]

runestone/parsons/prstest.html

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head lang="en">
4+
<meta charset="UTF-8">
5+
<title></title>
6+
<link rel="stylesheet" href="../assess/bower_components/jquery-ui/themes/base/jquery-ui.css"/>
7+
<link rel="stylesheet" href="bootstrap.min.css"/>
8+
<link rel="stylesheet" href="css/parsons.css"/>
9+
10+
<link rel="stylesheet" href="http://interactivepython.org/runestone/static/overview/_static/basic.css" type="text/css" />
11+
<link rel="stylesheet" href="http://interactivepython.org/runestone/static/overview/_static/pygments.css" type="text/css" />
12+
<link rel="stylesheet" href="http://interactivepython.org/runestone/static/overview/_static/bootstrap-3.0.0/css/bootstrap.min.css" type="text/css" />
13+
<link rel="stylesheet" href="http://interactivepython.org/runestone/static/overview/_static/video.css" type="text/css" />
14+
<link rel="stylesheet" href="http://interactivepython.org/runestone/static/overview/_static/poll.css" type="text/css" />
15+
<link rel="stylesheet" href="http://interactivepython.org/runestone/static/overview/_static/tabbedstuff.css" type="text/css" />
16+
<link rel="stylesheet" href="http://interactivepython.org/runestone/static/overview/_static/pytutor.css" type="text/css" />
17+
<link rel="stylesheet" href="http://interactivepython.org/runestone/static/overview/_static/modal-basic.css" type="text/css" />
18+
<link rel="stylesheet" href="http://interactivepython.org/runestone/static/overview/_static/codemirror.css" type="text/css" />
19+
<link rel="stylesheet" href="http://interactivepython.org/runestone/static/overview/_static/activecode.css" type="text/css" />
20+
<link rel="stylesheet" href="http://interactivepython.org/runestone/static/overview/_static/parsons.css" type="text/css" />
21+
<link rel="stylesheet" href="http://interactivepython.org/runestone/static/overview/_static/lib/prettify.css" type="text/css" />
22+
<link rel="stylesheet" href="http://interactivepython.org/runestone/static/overview/_static/codemirror.css" type="text/css" />
23+
<link rel="stylesheet" href="http://interactivepython.org/runestone/static/overview/_static/livecode.css" type="text/css" />
24+
<link rel="stylesheet" href="http://interactivepython.org/runestone/static/overview/_static/jquery-ui-1.10.3.custom.min.css" type="text/css" />
25+
<link rel="stylesheet" href="http://interactivepython.org/runestone/static/overview/_static/bootstrap-sphinx.css" type="text/css" />
26+
<link rel="stylesheet" href="http://interactivepython.org/runestone/static/overview/_static/user-highlights.css" type="text/css" />
27+
<link rel="stylesheet" href="http://interactivepython.org/runestone/static/overview/_static/runestone-custom-sphinx-bootstrap.css" type="text/css" />
28+
29+
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/lodash.js/0.10.0/lodash.min.js"></script>
30+
<script type="text/javascript" src="../assess/bower_components/jquery/dist/jquery.js"></script>
31+
<script type="text/javascript" src="../assess/bower_components/jquery-ui/jquery-ui.js"></script>
32+
<script type="text/javascript" src="http://interactivepython.org/runestone/static/thinkcspy/_static/bookfuncs.js"></script>
33+
<script type="text/javascript" src="http://interactivepython.org/runestone/static/overview/_static/lib/lis.js"></script>
34+
<script type="text/javascript" src="js/parsonsaux.js"></script>
35+
<script type="text/javascript" src="js/parsons.js"></script>
36+
<script type="text/javascript">
37+
eBookConfig = {};
38+
eBookConfig.host = 'http://interactivepython.org' ? 'http://interactivepython.org' : 'http://127.0.0.1:8000';
39+
eBookConfig.app = eBookConfig.host+'/runestone';
40+
eBookConfig.ajaxURL = eBookConfig.app+'/ajax/';
41+
eBookConfig.course = 'thinkcspy';
42+
eBookConfig.logLevel = 10;
43+
eBookConfig.loginRequired = false
44+
eBookConfig.build_info = "3.1.1";
45+
eBookConfig.isLoggedIn = false;
46+
</script>
47+
48+
49+
</head>
50+
<body>
51+
<pre data-component="parsons" id="example1">
52+
<span data-question>This is the question</span>
53+
x = 0
54+
---
55+
for i in range(10)
56+
---
57+
x = x + 1
58+
---
59+
print("Suck it I'm awesome")
60+
</pre>
61+
</body>
62+
63+
</html>

0 commit comments

Comments
 (0)