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

Commit 505f501

Browse files
committed
Merge pull request #3 from RunestoneInteractive/shortanswer
Add shortanswer directive
2 parents 407d1f6 + 21410fb commit 505f501

File tree

7 files changed

+208
-3
lines changed

7 files changed

+208
-3
lines changed

CONTRIBUTING.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,7 @@ Provide an example
2626
------------------
2727

2828
The folder ``runestone/common/project_template/_sources`` folder is a great place to add a file
29-
that demonstrates your new feature or component in action.
29+
that demonstrates your new feature or component in action.
30+
31+
In fact you should provide two examples whenever possible to demonstrate that you can have
32+
multiple instances of your component on a single web page.

runestone/common/js/bookfuncs.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1137,3 +1137,34 @@ function injectCodeCoach(div_id) {
11371137
'div_id': div_id
11381138
});
11391139
}
1140+
1141+
var storage = {
1142+
set: function(directive, value) {
1143+
localStorage.setItem(directive+"_value", value);
1144+
localStorage.setItem(directive+"_timestamp", $.now());
1145+
},
1146+
remove: function(directive) {
1147+
localStorage.removeItem(directive+"_value");
1148+
localStorage.removeItem(directive+"_timestamp");
1149+
},
1150+
get: function(directive) {
1151+
return localStorage.getItem(directive+"_value");
1152+
},
1153+
has: function(directive) {
1154+
return localStorage.getItem(directive+"_value") !== null;
1155+
},
1156+
// Tests whether the server has the newer version
1157+
is_new: function(directive, server_time) {
1158+
var stored_time = localStorage.getItem(directive+"_timestamp");
1159+
return (server_time >= stored_time+5000);
1160+
},
1161+
};
1162+
1163+
var timers = {};
1164+
function addDelay(directive, action, delay) {
1165+
if (delay === undefined) {
1166+
delay = 400;
1167+
}
1168+
clearTimeout(timers[directive]);
1169+
timers[directive] = setTimeout(action, delay);
1170+
}

runestone/shortanswer/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .shortanswer import *

runestone/shortanswer/css/shortanswer.css

Whitespace-only changes.
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
2+
3+
/**
4+
* If the divid is in the localStorage, then assume it was put there in a panic
5+
* Check against the server to find which is more recent, and use that.
6+
*
7+
*/
8+
function loadJournal(directive_id) {
9+
if (storage.has(directive_id)) {
10+
var solution = $('#' + directive_id + '_solution');
11+
solution.text(storage.get(directive_id));
12+
}
13+
14+
/*directiveRemoteCommand('get_journal_entry', directive_id, {},
15+
function(data) {
16+
var solution = $('#'+directive_id+'_solution');
17+
solution.text(data.solution);
18+
if (storage.has(directive_id)) {
19+
if (storage.is_new(directive_id, data.timestamp)) {
20+
storage.remove(directive_id);
21+
} else {
22+
solution.text(storage.get(directive_id));
23+
submitJournal(directive_id);
24+
}
25+
}
26+
},
27+
function(data) {
28+
console.log(data.message);
29+
}); */
30+
}
31+
32+
function submitJournal(directive_id) {
33+
var value = $('#'+directive_id+'_solution').val();
34+
storage.set(directive_id, value);
35+
/*
36+
directiveRemoteCommand('set_journal_entry', directive_id, {'solution': value},
37+
function(data) {
38+
storage.remove(directive_id);
39+
},
40+
function(data) {
41+
console.log(data.message);
42+
}); */
43+
logBookEvent({'event': 'shortanswer', 'act': JSON.stringify(value), 'div_id': directive_id});
44+
}
45+
46+
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# Copyright (C) 2011 Bradley N. Miller
2+
#
3+
# This program is free software: you can redistribute it and/or modify
4+
# it under the terms of the GNU General Public License as published by
5+
# the Free Software Foundation, either version 3 of the License, or
6+
# (at your option) any later version.
7+
#
8+
# This program is distributed in the hope that it will be useful,
9+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11+
# GNU General Public License for more details.
12+
#
13+
# You should have received a copy of the GNU General Public License
14+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
15+
#
16+
__author__ = 'acbart'
17+
18+
import os
19+
20+
from docutils import nodes
21+
from docutils.parsers.rst import directives
22+
from docutils.parsers.rst import Directive
23+
from runestone.assess import Assessment
24+
25+
def setup(app):
26+
app.add_directive('shortanswer', JournalDirective)
27+
28+
app.add_node(JournalNode, html=(visit_journal_node, depart_journal_node))
29+
30+
app.add_javascript('shortanswer.js')
31+
app.add_stylesheet('shortanswer.css')
32+
33+
34+
TEXT = """
35+
<div id='%(divid)s' class='journal alert alert-%(optional)s'>
36+
<form id='%(divid)s_journal' name='%(divid)s_journal' action="">
37+
<fieldset>
38+
<legend>Short Answer</legend>
39+
<div class='journal-question'>%(qnum)s: %(content)s</div>
40+
<div id='%(divid)s_journal_input'>
41+
<div class='journal-options'>
42+
<label class='radio-inline'>
43+
<textarea id='%(divid)s_solution' class="form-control" style="display:inline; width: 530px;"
44+
rows='4' cols='50'></textarea>
45+
</label>
46+
</div><br />
47+
<div><button class="btn btn-default" onclick="submitJournal('%(divid)s');">Save</button></div>
48+
Instructor's Feedback:
49+
<div class='journal-options' style='padding-left:20px'>
50+
<div class='bg-info form-control' style='width:530px; background-color: #eee; font-style:italic'
51+
id='%(divid)s_feedback'>
52+
There is no feedback yet.
53+
</div>
54+
</div><br />
55+
</div>
56+
</fieldset>
57+
</form>
58+
<div id='%(divid)s_results'></div>
59+
<script type='text/javascript'>
60+
// check if the user has already answered this journal
61+
$(function() {
62+
loadJournal('%(divid)s');
63+
});
64+
</script>
65+
</div>
66+
"""
67+
68+
class JournalNode(nodes.General, nodes.Element):
69+
def __init__(self, options):
70+
super(JournalNode, self).__init__()
71+
self.journalnode_components = options
72+
73+
def visit_journal_node(self, node):
74+
div_id = node.journalnode_components['divid']
75+
back, subchapter = os.path.split(os.path.splitext(node.source.lower())[0])
76+
back, chapter = os.path.split(back)
77+
content = ''
78+
components = dict(node.journalnode_components)
79+
components.update({'divid': div_id,
80+
'subchapter': subchapter,
81+
'chapter': chapter})
82+
res = TEXT % components
83+
self.body.append(res)
84+
85+
def depart_journal_node(self,node):
86+
pass
87+
88+
89+
class JournalDirective(Assessment):
90+
required_arguments = 1 # the div id
91+
optional_arguments = 0
92+
final_argument_whitespace = True
93+
has_content = True
94+
option_spec = {'optional': directives.flag}
95+
96+
node_class = JournalNode
97+
98+
def run(self):
99+
# Raise an error if the directive does not have contents.
100+
101+
self.assert_has_content()
102+
103+
self.options['optional'] = 'success' if 'optional' in self.options else 'warning'
104+
self.options['divid'] = self.arguments[0]
105+
self.options['content'] = "<p>".join(self.content)
106+
self.options['qnum'] = self.getNumber()
107+
journal_node = JournalNode(self.options)
108+
109+
return [journal_node]
110+
111+

setup.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
setup(
77
name='runestone',
88
description='Sphinx extensions for writing interactive documents.',
9-
version='2.0a1',
9+
version='2.0a2',
1010
author = 'Brad Miller',
1111
author_email = '[email protected]',
1212
packages= find_packages(),
@@ -19,7 +19,20 @@
1919
url = 'https://github.com/RunestoneInteractive/RunestoneTools',
2020
download_url = 'https://github.com/RunestoneInteractive/RunestoneTools/tarball/2.0a1',
2121
keywords = ['runestone', 'sphinx', 'ebook'], # arbitrary keywords
22-
classifiers = [],
22+
classifiers=('Development Status :: 5 - Production/Stable',
23+
'Environment :: Console',
24+
'Environment :: Plugins',
25+
'Environment :: Web Environment',
26+
'Framework :: Sphinx :: Extension',
27+
'Intended Audience :: Education',
28+
'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)',
29+
'Operating System :: MacOS',
30+
'Operating System :: Unix',
31+
'Programming Language :: Python',
32+
'Programming Language :: Python :: 2.7',
33+
'Programming Language :: Python :: 3.4',
34+
'Topic :: Education',
35+
'Topic :: Text Processing :: Markup'),
2336
# data_files=[('common',['runestone/common/*']),
2437
# ('project/template', ['newproject_copy_me/*'])
2538
# ],

0 commit comments

Comments
 (0)