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

Commit 455cd7c

Browse files
committed
Merge branch 'riknos314-shortanswer'
2 parents 2c4c8bc + d46c88a commit 455cd7c

File tree

4 files changed

+195
-71
lines changed

4 files changed

+195
-71
lines changed

runestone/common/js/bookfuncs.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ function gotUser(data, status, whatever) {
9393
} else {
9494
if (!caughtErr) {
9595
mess = d.email;
96+
eBookConfig.email = d.email;
9697
eBookConfig.isLoggedIn = true;
9798
eBookConfig.cohortId = d.cohortId;
9899
$(document).trigger("runestone:login")

runestone/shortanswer/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<h2>Short Answer</h2>
2+
3+
```html
4+
<p data-component="shortanswer" data-optional id="example1">What is the best thing about the color blue?</p>
5+
```
6+
7+
The <code>p</code> tag represents the entire Short Answer component to be rendered.
8+
(more info about the use)
9+
10+
11+
Option spec:
12+
13+
<ul>
14+
<li><code>data-component="shortanswer"</code> Identifies this as a Short Answer component</li>
15+
<li><code>id</code> Must be unique in the document</li>
16+
<li><code>data-optional</code> Makes this component optional for the student to answer--it isn't required.</li>
17+
</ul>

runestone/shortanswer/js/shortanswer.js

100755100644
Lines changed: 170 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,185 @@
1+
/*==========================================
2+
======= Master shortanswer.js ========
3+
============================================
4+
=== This file contains the JS for ===
5+
=== the Runestone shortanswer component. ===
6+
============================================
7+
=== Created by ===
8+
=== Isaiah Mayerchak ===
9+
=== 7/2/15 ===
10+
==========================================*/
111

12+
var saList = {}; // Dictionary that contains all instances of shortanswer objects
213

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));
14+
15+
function ShortAnswer (opts) {
16+
if (opts) {
17+
this.init(opts);
1218
}
19+
}
1320

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+
ShortAnswer.prototype = new RunestoneBase();
22+
23+
/*========================================
24+
== Initialize basic ShortAnswer attributes ==
25+
========================================*/
26+
ShortAnswer.prototype.init = function (opts) {
27+
RunestoneBase.apply(this, arguments);
28+
var orig = opts.orig; // entire <p> element that will be replaced by new HTML
29+
this.useRunestoneServies = opts.useRunestoneServices || eBookConfig.useRunestoneServices;
30+
this.origElem = orig;
31+
this.divid = orig.id;
32+
this.question = this.origElem.innerHTML;
33+
34+
this.optional = false;
35+
if ($(this.origElem).is("[data-optional]")) {
36+
this.optional = true;
37+
}
38+
39+
this.renderHTML();
40+
this.loadJournal();
41+
};
42+
43+
ShortAnswer.prototype.renderHTML = function() {
44+
this.containerDiv = document.createElement("div");
45+
this.containerDiv.id = this.divid;
46+
if (this.optional) {
47+
$(this.containerDiv).addClass("journal alert alert-success");
2148
} else {
22-
solution.text(storage.get(directive_id));
23-
submitJournal(directive_id);
49+
$(this.containerDiv).addClass("journal alert alert-warning");
2450
}
25-
}
26-
},
27-
function(data) {
28-
console.log(data.message);
29-
}); */
30-
}
3151

32-
function submitJournal(directive_id) {
33-
var value = $('#'+directive_id+'_solution').val();
34-
storage.set(directive_id, value);
52+
this.newForm = document.createElement("form");
53+
this.newForm.id = this.divid + "_journal";
54+
this.newForm.name = this.newForm.id;
55+
this.newForm.action = "";
56+
this.containerDiv.appendChild(this.newForm);
57+
58+
this.fieldSet = document.createElement("fieldset");
59+
this.newForm.appendChild(this.fieldSet);
60+
61+
this.legend = document.createElement("legend");
62+
this.legend.innerHTML = "Short Answer";
63+
this.fieldSet.appendChild(this.legend);
64+
65+
this.firstLegendDiv = document.createElement("div");
66+
this.firstLegendDiv.innerHTML = this.question;
67+
$(this.firstLegendDiv).addClass("journal-question");
68+
this.fieldSet.appendChild(this.firstLegendDiv);
69+
70+
this.jInputDiv = document.createElement("div");
71+
this.jInputDiv.id = this.divid + "_journal_input";
72+
this.fieldSet.appendChild(this.jInputDiv);
73+
74+
this.jOptionsDiv = document.createElement("div");
75+
$(this.jOptionsDiv).addClass("journal-options");
76+
this.jInputDiv.appendChild(this.jOptionsDiv);
77+
78+
this.jLabel = document.createElement("label");
79+
$(this.jLabel).addClass("radio-inline");
80+
this.jOptionsDiv.appendChild(this.jLabel);
81+
82+
this.jTextArea = document.createElement("textarea");
83+
this.jTextArea.id = this.divid + "_solution";
84+
$(this.jTextArea).css("display:inline, width:530px");
85+
$(this.jTextArea).addClass("form-control");
86+
this.jTextArea.rows = 4;
87+
this.jTextArea.cols = 50;
88+
this.jLabel.appendChild(this.jTextArea);
89+
90+
this.fieldSet.appendChild(document.createElement("br"));
91+
92+
this.buttonDiv = document.createElement("div");
93+
this.fieldSet.appendChild(this.buttonDiv);
94+
95+
this.submitButton = document.createElement("button");
96+
$(this.submitButton).addClass("btn btn-default");
97+
this.submitButton.type = "button";
98+
this.submitButton.textContent = "Save";
99+
this.submitButton.onclick = function () {
100+
this.submitJournal();
101+
}.bind(this);
102+
this.buttonDiv.appendChild(this.submitButton);
103+
104+
this.randomSpan = document.createElement("span");
105+
this.randomSpan.innerHTML = "Instructor's Feedback";
106+
this.fieldSet.appendChild(this.randomSpan);
107+
108+
this.otherOptionsDiv = document.createElement("div");
109+
$(this.otherOptionsDiv).css("padding-left:20px");
110+
$(this.otherOptionsDiv).addClass("journal-options");
111+
this.fieldSet.appendChild(this.otherOptionsDiv);
112+
113+
this.feedbackDiv = document.createElement("div");
114+
$(this.feedbackDiv).addClass("bg-info form-control");
115+
$(this.feedbackDiv).css("width:530px, background-color:#eee, font-style:italic");
116+
this.feedbackDiv.id = this.divid + "_feedback";
117+
this.feedbackDiv.innerHTML = "There is no feedback yet.";
118+
this.otherOptionsDiv.appendChild(this.feedbackDiv);
119+
120+
this.fieldSet.appendChild(document.createElement("br"));
121+
122+
$(this.origElem).replaceWith(this.containerDiv);
123+
};
124+
125+
ShortAnswer.prototype.submitJournal = function () {
126+
var value = $("#"+this.divid+"_solution").val();
127+
localStorage.setItem(this.divid, value);
35128
/*
36-
directiveRemoteCommand('set_journal_entry', directive_id, {'solution': value},
129+
directiveRemoteCommand("set_journal_entry", this.divid, {"solution": value},
37130
function(data) {
38-
storage.remove(directive_id);
131+
storage.remove(this.divid);
39132
},
40133
function(data) {
41134
console.log(data.message);
42135
}); */
43-
this.logBookEvent({'event': 'shortanswer', 'act': JSON.stringify(value), 'div_id': directive_id});
44-
}
136+
this.logBookEvent({'event': 'shortanswer', 'act': JSON.stringify(value), 'div_id': this.divid});
137+
};
138+
139+
ShortAnswer.prototype.loadJournal = function () {
140+
141+
// Ask the server to send the latest
142+
var loadAnswer = function(data,status,whatever) {
143+
var len = localStorage.length;
144+
var answer = {};
145+
if (! jQuery.isEmptyObject(data)) {
146+
answer = data;
147+
} else {
148+
answer.answer = "";
149+
}
150+
var solution = $("#" + this.divid + "_solution");
151+
if (len > 0) {
152+
var ex = storage.get(this.divid);
153+
if (ex !== null ) {
154+
if (! storage.is_new(answer.divid, new Date(answer.timestamp))) {
155+
solution.text(storage.get(this.divid));
156+
// now send the newer answer to the server...
157+
} else {
158+
solution.text(answer.answer);
159+
}
160+
} else {
161+
solution.text(answer.answer);
162+
}
163+
} else {
164+
solution.text(answer.answer);
165+
}
166+
}.bind(this);
167+
var data = {'div_id' : this.divid};
168+
if (this.useRunestoneServies) {
169+
jQuery.get(eBookConfig.ajaxURL + 'getlastanswer', data, loadAnswer);
170+
} else {
171+
loadAnswer({},null,null);
172+
}
173+
};
174+
45175

176+
/*=================================
177+
== Find the custom HTML tags and ==
178+
== execute our code on them ==
179+
=================================*/
180+
$(document).ready(function () {
181+
$("[data-component=shortanswer]").each(function (index) {
182+
saList[this.id] = new ShortAnswer({"orig": this, 'useRunestoneServices': eBookConfig.useRunestoneServices});
183+
});
46184

185+
});

runestone/shortanswer/shortanswer.py

Lines changed: 7 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
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__ = 'acbart'
17-
16+
__author__ = 'isaiahmayerchak'
17+
#acbart did most of this code, I mostly just changed the template
1818
import os
1919

2020
from docutils import nodes
@@ -28,41 +28,10 @@ def setup(app):
2828
app.add_node(JournalNode, html=(visit_journal_node, depart_journal_node))
2929

3030
app.add_javascript('shortanswer.js')
31-
app.add_stylesheet('shortanswer.css')
3231

3332

3433
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>
34+
<p data-component="shortanswer" id=%(divid)s %(optional)s>%(qnum)s: %(content)s</p>
6635
"""
6736

6837
class JournalNode(nodes.General, nodes.Element):
@@ -96,16 +65,14 @@ class JournalDirective(Assessment):
9665
node_class = JournalNode
9766

9867
def run(self):
99-
# Raise an error if the directive does not have contents.
100-
68+
# Raise an error if the directive does not have contents.
69+
10170
self.assert_has_content()
102-
103-
self.options['optional'] = 'success' if 'optional' in self.options else 'warning'
71+
72+
self.options['optional'] = 'data-optional' if 'optional' in self.options else ''
10473
self.options['divid'] = self.arguments[0]
10574
self.options['content'] = "<p>".join(self.content)
10675
self.options['qnum'] = self.getNumber()
10776
journal_node = JournalNode(self.options)
10877

10978
return [journal_node]
110-
111-

0 commit comments

Comments
 (0)