Commit ab593b98 authored by platypro's avatar platypro

Improved web front-end. Added answer options. Added a test exercise.

parent baaf2496
{
"name" : "Test",
"calculator" : "banned",
}
{
"name" : "Test set",
"questions": [{ "id":"test" }]
}
createWidget("numberInput", 45.1, {hint="test integer (45)", mindp=0})
createWidget("numberInput", 45.1, {hint="test float (45.1)", mindp=1})
createWidget("textInput", "AaAaAa", {hint="test case-sensitive text (AaAaAa)", caps=true})
createWidget("textInput", "AaAaAa", {hint="test case-insensitive text (aaaaaa)", caps=false})
......@@ -216,41 +216,12 @@ bool session_common_process(PG_STATE* state, WJElement request, WJWriter respons
USER* u;
if((u = getUserAndAssert(request, state, response)) && u->questionAt)
{
WJElement usolution_orig = WJEArray(request, KEY_SOLUTION, WJE_GET);
WJElement usolution, usolution_next;
WJElement solution = u->questionAt->solutions->child;
uint8_t success = solution->parent->count;
SCP_ASSERT(usolution_orig, "No answer given!");
WJElement usolution, usolution_orig = WJEArray(request, KEY_SOLUTION, WJE_GET);
SCP_ASSERT(usolution_orig, "No answer given!");
WJWString(KEY_ACTION, ACTION_QUESTION_CHECK, true, response);
while(solution)
{
for(
usolution = usolution_orig->child, usolution_next = usolution->next;
usolution;
//This is a C hack...
(usolution = usolution_next) && (usolution_next = usolution_next->next)
)
{
if(WJEUInt32(usolution, KEY_ID, WJE_GET, 0)
== WJEUInt32(solution, KEY_ID, WJE_GET, 1))
{
//TODO: Make this more flexible
if(!strcmp(
WJEString(usolution, KEY_KEY, WJE_GET, NULL),
WJEString(solution, KEY_KEY, WJE_GET, NULL)
))
{
success--;
WJEDetach(usolution);
WJECloseDocument(usolution);
}
}
}
solution = solution->next;
}
if(!success) // There are answers left
if(!exercise_checkAnswer(u->questionAt, &usolution_orig))
WJWString(KEY_RESULT, "correct", true, response);
else
{
......
......@@ -16,6 +16,8 @@
#include <time.h>
#include <libgen.h>
#include <alloca.h>
#include <ctype.h>
#include <math.h>
#include <pmustache/template.h>
#include <pmustache/provider.wje.h>
......@@ -309,6 +311,70 @@ bool exercise_writeWidgets(struct PG_State* state, QUESTION* question, WJWriter
return true;
}
bool widget_checkAnswer(WIDGET* w, char* ans, char* uans)
{
if(!strcmp(w->type, "textInput"))
{
if(!WJEBool(w->options, "caps", WJE_GET, FALSE))
{
while (tolower(*ans) && (tolower(*ans) == tolower(*uans)))
ans++, uans++;
return (tolower(*ans) == tolower(*uans));
}
return !strcmp(ans, uans);
}
if(!strcmp(w->type, "numberInput"))
{
char* end;
double nuans = strtod(uans, &end);
if(*end) return false;
double nans = strtod( ans, &end);
if(*end) return false;
uint32_t mindp = WJEUInt32(w->options, "mindp", WJE_GET, FALSE);
double exponent = pow(10, mindp);
return (round(nuans * exponent) == round(nans * exponent));
}
return false;
}
uint8_t exercise_checkAnswer(QUESTION* question, WJElement* usolution_orig)
{
WJElement usolution, usolution_next;
WJElement solution = question->solutions->child;
uint8_t success = solution->parent->count;
while(solution)
{
uint32_t sid = WJEUInt32(solution, KEY_ID, WJE_GET, 1);
for(
usolution = (*usolution_orig)->child, usolution_next = usolution->next;
usolution;
//This is to allow deletions but still continue the loop
(usolution = usolution_next) && (usolution_next = usolution_next->next)
)
{
if(sid == WJEUInt32(usolution, KEY_ID, WJE_GET, 0))
{
WIDGET* widget = question->widgets;
while(widget)
{
if(widget->id == sid
&& widget_checkAnswer(widget, WJEString(solution, KEY_KEY, WJE_GET, NULL), WJEString(usolution, KEY_KEY, WJE_GET, NULL)))
{
success--;
WJEDetach(usolution);
WJECloseDocument(usolution);
}
widget = widget->next;
}
}
}
solution = solution->next;
}
return success;
}
bool exercise_destroyQuestion(QUESTION* quiz)
{
WJECloseDocument(quiz->solutions);
......
......@@ -115,6 +115,7 @@ extern bool exercise_loadAll(struct PG_State* state);
extern char* exercise_loadOne(struct PG_State* state, char* set);
extern PROBLEMSET* exercise_getSet(struct PG_State* state, char* name);
extern QUESTION* exercise_mkQuestion(struct PG_State* state, PROBLEMSET* pset);
extern uint8_t exercise_checkAnswer(QUESTION* question, WJElement* solutions);
extern bool exercise_writeWidgets(struct PG_State* state, QUESTION* question, WJWriter w);
extern bool exercise_destroyQuestion(QUESTION* question);
extern void exercise_cleanup(struct PG_State* state);
......
......@@ -158,11 +158,21 @@ function processRequest()
elem = document.createElement("input");
var lbl = document.createElement("label");
if(widget.options.hint)
lbl.innerHTML = widget.options.hint + " "
elem.classList.add("numberInput");
{
lbl.classList.add("labelHasHint");
lbl.innerHTML = widget.options.hint
}
elem.step = "any";
elem.type = "number";
elem.id = widget.id;
lbl.appendChild(elem);
if(widget.options.mindp)
{
ttip = document.createElement("span");
ttip.classList.add("tooltip");
ttip.innerText = "Answer to a minimum of " + widget.options.mindp +" decimal place" + (widget.options.mind > 1 ? "s." : ".");
lbl.appendChild(ttip);
}
questionArea.appendChild(lbl);
if(!focus) {elem.focus(); focus = true;}
break;
......@@ -170,11 +180,22 @@ function processRequest()
elem = document.createElement("input");
var lbl = document.createElement("label");
if(widget.options.hint)
lbl.innerHTML = widget.options.hint + " "
elem.classList.add("numberInput");
{
lbl.classList.add("labelHasHint");
lbl.innerHTML = widget.options.hint;
}
elem.type = "text";
elem.id = widget.id;
lbl.appendChild(elem);
if(widget.options.caps)
{
ttip = document.createElement("span");
ttip.classList.add("tooltip");
ttip.innerText = "This answer is CaSe SeNsItIvE.";
lbl.appendChild(ttip);
}
questionArea.appendChild(lbl);
if(!focus) {elem.focus(); focus = true;}
break;
......@@ -198,6 +219,11 @@ function processRequest()
var answerStatus = document.getElementById("answerStatus");
var questionArea = document.getElementById("questionArea");
for(oldelement of document.getElementsByClassName("controlError"))
{
oldelement.classList.remove("controlError");
}
if(data.result === "correct")
{
answerStatus.classList.add("label_good");
......@@ -212,11 +238,6 @@ function processRequest()
answerStatus.classList.add("label_bad");
answerStatus.classList.remove("label_good");
for(oldelement of document.getElementsByClassName("controlError"))
{
oldelement.classList.remove("controlError");
}
data.error.forEach(function(errelement)
{
document.getElementById(errelement).classList.add("controlError");
......@@ -244,15 +265,15 @@ function processRequest()
else
{
setAnswerCircle("i");
var solTitle = document.createElement("b");
solTitle.innerText = "Solution";
var solTitle = document.createElement("strong");
solTitle.innerText = "Solution:";
hintArea.appendChild(solTitle);
btn_getHint.disabled = true;
btn_getHint.value = "No more hints";
for(element of data.solution)
{
var label = document.getElementById(element.id);
elem.innerText += label.parentNode.textContent + " " + element.key;
elem.innerHTML += label.parentNode.innerText + "<strong>" + element.key + "</strong>";
elem.innerHTML += "<br>";
}
}
......
......@@ -19,7 +19,7 @@
<div id="eNameText"></div>
</div>
<div class="MidControls">
<div id="questionArea">
<div id="questionArea" style="margin:0 auto">
</div>
<noscript>
Javascript is needed for the quiz, sorry.
......
......@@ -71,13 +71,6 @@ body {
margin-top: auto;
}
.numberInput {
border: 2px solid #b3ffd9;
width:8em;
-webkit-appearance: textfield;
-moz-appearance: textfield;
}
.probieQuizArea #eCalcText {
text-align: left;
flex-grow: 1;
......@@ -114,6 +107,18 @@ svg,p {
border: 2px solid #CC8383;
}
/* Label has a hint, align it. */
.labelHasHint
{
text-align: right;
}
#questionArea label
{
display: block;
margin: 0.5em 1em;
}
input[type="radio"]+label
{
border: 0.1em solid white;
......@@ -135,6 +140,44 @@ input[type="radio"]
display:none;
}
input[type="number"],input[type="text"] {
border: 2px solid #b3ffd9;
width:8em;
margin-left:0.3em;
-webkit-appearance: textfield;
-moz-appearance: textfield;
}
input+.tooltip {
visibility: hidden;
display:none;
}
input:focus + .tooltip {
visibility: visible;
display: inline;
position: absolute;
text-align:center;
width: 10em;
margin-left:0.3em;
padding:0.2em;
border: 0.2em solid #FF9500;
z-index: 1;
background-color: black;
border-radius: 0 0.5em 0.5em 0.5em;
}
.tooltip:after {
content: '';
position: absolute;
left: 0; top: 0;
border: 0.7em solid transparent;
border-right-color: #FF9500;
border-left: 0;
margin-top: -0.2em;
margin-left: -0.85em;
}
/* Question indicator stuff */
.probieQuizArea #questionsLeft
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment