This commit is contained in:
Wiesner András 2024-03-06 20:29:22 +01:00
parent 2c9a744667
commit 319b3410ba
12 changed files with 709 additions and 111 deletions

View File

@ -15,15 +15,24 @@ if (!get_autologin_state()) {
<head>
<meta charset="UTF-8">
<title>SpreadQuiz</title>
<script src="js/spreadquiz.js"></script>
<script src="js/req.js"></script>
<script src="js/o.js"></script>
<script src="js/common.js"></script>
<script src="js/spreadquiz.js"></script>
<script src="js/default_frame.js"></script>
<link rel="stylesheet" href="style/spreadquiz.css">
</head>
<body>
<section id="game_list_panel">
</section>
<section class="window" shown="false" id="results_window">
<section class="window-inner">
<section id="results_overview_display">
</section>
<input type="button" value="Bezárás" onclick="hide('results_window')">
</section>
</section>
<script>

View File

@ -13,7 +13,7 @@ const DEFAULT_GAME_PROPERTIES = [
function create_game(string $name, string $owner, string $description = "", array $properties = DEFAULT_GAME_PROPERTIES, array $contributors = [], array $challenges = []): bool
{
global $testdb;
global $gamedb;
$game_data = [
"name" => $name,
"owner" => $owner,
@ -23,7 +23,7 @@ function create_game(string $name, string $owner, string $description = "", arra
"properties" => $properties,
"groups" => [],
];
$game_data = $testdb->insert($game_data);
$game_data = $gamedb->insert($game_data);
// prepare game context
$id = $game_data["_id"];
@ -35,25 +35,25 @@ function create_game(string $name, string $owner, string $description = "", arra
function get_game(string $gameid): array
{
global $testdb;
return $testdb->findById($gameid);
global $gamedb;
return $gamedb->findById($gameid);
}
function update_game(array $game_data)
{
global $testdb;
$testdb->update($game_data);
global $gamedb;
$gamedb->update($game_data);
}
function delete_game(string $gameid)
{
global $testdb;
global $gamedb;
$game_data = get_game($gameid);
if (count($game_data) != 0) {
foreach ($game_data["groups"] as $groupid) {
change_group_game_assignments($groupid, null, $gameid);
}
$testdb->deleteById($gameid);
$gamedb->deleteById($gameid);
}
}
@ -68,18 +68,18 @@ function change_game_group_assignments(string $gid, $groupname_add, $groupname_r
function get_all_games()
{
global $testdb;
return $testdb->findAll();
global $gamedb;
return $gamedb->findAll();
}
function get_all_game_data_by_contributor_nickname(string $nickname): array
{
global $testdb;
global $gamedb;
$game_headers = [];
if ($nickname !== "*") {
$game_data_array = $testdb->findBy([["owner", "=", $nickname], "OR", ["contributors", "CONTAINS", $nickname]]);
$game_data_array = $gamedb->findBy([["owner", "=", $nickname], "OR", ["contributors", "CONTAINS", $nickname]]);
} else {
$game_data_array = $testdb->findAll();
$game_data_array = $gamedb->findAll();
}
foreach ($game_data_array as $game_data) {
$game_headers[] = $game_data;

View File

@ -16,6 +16,7 @@ require_once "common_func.php";
require_once "usermgr.php";
require_once "groupmgr.php";
require_once "gamemgr.php";
require_once "testmgr.php";
$action = $_REQUEST["action"];
@ -83,6 +84,61 @@ switch ($action) {
$result = json_encode($games_by_groups);
}
break;
case "start_or_continue_test":
{
$gameid = trim($_REQUEST["gameid"] ?: "");
$testid = create_or_continue_test($gameid, $nickname);
$result = $testid;
}
break;
case "get_results_overview":
{
$gameid = trim($_REQUEST["gameid"] ?: "");
$concluded_tests = get_concluded_tests($gameid, $nickname);
$overviews = [];
foreach ($concluded_tests as $ct) {
$overview = [
"testid" => $ct["_id"],
"start_time" => $ct["start_time"],
"end_time" => $ct["end_time"],
...$ct["summary"]
];
$overviews[] = $overview;
}
$result = json_encode($overviews);
}
break;
}
// test-related queries
if ((($testid = trim($_REQUEST["testid"] ?: "")) !== "") &&
((count($test_data = get_test($testid))) > 0) &&
($test_data["nickname"] === $nickname)) {
// update the test if timed
update_timed_tests([ $test_data ]);
switch ($action) {
case "get_test":
{
$test_data_with_current_time = $test_data;
$test_data_with_current_time["current_time"] = time();
$result = json_encode($test_data_with_current_time);
}
break;
case "save_answer":
{
$chidx = $_REQUEST["challenge_index"];
$answeridx = $_REQUEST["answer_index"];
save_answer($testid, $chidx, $answeridx);
}
break;
case "submit_test":
{
conclude_test($testid);
}
break;
}
}
// creator or quizmaster actions

5
js/common.js Normal file
View File

@ -0,0 +1,5 @@
function unix_time_to_human_readable(tunix) {
const date = new Date(Number(tunix) * 1000);
return date.getFullYear() + ". " + String(date.getMonth() + 1).padStart(2, "0") + ". " + String(date.getDate()).padStart(2, "0") + ". "
+ String(date.getHours()).padStart(2, "0") + ":" + String(date.getMinutes()).padStart(2, "0") + ":" + String(date.getSeconds()).padStart(2, "0");
}

View File

@ -9,21 +9,103 @@ function list_available_games() {
games_by_groups.forEach((game_collection) => {
let group_box = document.createElement("section");
group_box.classList.add("group-box");
let group_box_caption_container = document.createElement("span");
group_box_caption_container.classList.add("group-box-caption-container");
let group_box_caption = document.createElement("span");
group_box_caption.classList.add("group-box-caption");
group_box_caption.innerHTML = game_collection["groupname"];
group_box_caption_container.append(group_box_caption);
let group_box_inner = document.createElement("section");
group_box_inner.classList.add("group-box-inner");
group_box.append(group_box_caption, group_box_inner);
group_box.append(group_box_caption_container, group_box_inner);
game_collection["games"].forEach((game) => {
let game_box = document.createElement("section");
game_box.classList.add("game-box");
game_box.innerHTML = game["name"];
group_box_inner.appendChild(game_box);
let game_box = document.createElement("section");
game_box.classList.add("game-box");
game_box.addEventListener("click", () => {
start_or_continue_test(game["_id"]);
});
let game_box_caption = document.createElement("section");
game_box_caption.classList.add("game-box-caption");
game_box_caption.innerHTML = game["name"];
let list_results_btn = document.createElement("section");
list_results_btn.classList.add("list-results-btn", "material", "btn");
list_results_btn.addEventListener("click", (event) => {
event.stopPropagation();
list_corresponding_results(game["_id"]);
});
list_results_btn.innerHTML = "&#xef3e";
game_box.append(game_box_caption, list_results_btn);
group_box_inner.append(game_box);
});
game_list_panel.appendChild(group_box);
});
});
}
function start_or_continue_test(gameid) {
let req = {
action: "start_or_continue_test",
gameid: gameid
}
request(req).then(resp => {
if (resp.length > 0) // response is non-zero
{
open_test(resp);
}
});
}
function open_test(testid) {
window.open("testground.php?testid=" + testid, "_new");
}
function list_corresponding_results(gameid) {
let results_overview_display = document.getElementById("results_overview_display");
results_overview_display.innerHTML = "";
let req = {
action: "get_results_overview",
gameid: gameid
};
request(req).then(resp => {
let results = JSON.parse(resp);
let n = results.length;
if (n === 0) { // don't continue with processing an empty array
return;
}
results.sort((a,b) => {return Number(b["testid"]) - Number(a["testid"])}); // sort records by ID
results.forEach((record) => {
let test_summary_record = document.createElement("section");
test_summary_record.classList.add("test-summary-record");
test_summary_record.addEventListener("click", () => {
open_test(record["testid"]);
});
let sequence_number_sec = document.createElement("section");
sequence_number_sec.classList.add("summary-sequence-number");
sequence_number_sec.innerText = "#" + n;
let duration_sec = document.createElement("section");
duration_sec.classList.add("summary-duration");
let start_time = unix_time_to_human_readable(record["start_time"]);
let end_time = unix_time_to_human_readable(record["end_time"]);
duration_sec.innerHTML = `${start_time}-<br>${end_time}<br>${record["correct_answer_n"]}/${record["challenge_n"]}`;
let percentage = document.createElement("section");
percentage.classList.add("summary-percentage");
let r = Math.floor((record["correct_answer_n"] / record["challenge_n"]) * 100);
percentage.innerHTML = `${r}%`;
test_summary_record.append(sequence_number_sec, duration_sec, percentage);
results_overview_display.appendChild(test_summary_record);
n--;
});
show("results_window")
});
}

8
js/main.js Normal file
View File

@ -0,0 +1,8 @@
function logout() {
let req = {
action: "logout"
};
request(req).then(resp => {
location.href = "login.php";
});
}

View File

@ -1,30 +1,164 @@
function populate_test(test_id) {
let test_display = document.getElementById("test_display");
let TEST_DATA = {}
let INTERVAL_HANDLE = null;
function print_time_left(t) {
let timerS = document.getElementById("timer");
let hours = Math.floor(t / 3600);
t -= hours * 3600;
let minutes = Math.floor(t / 60);
t -= minutes * 60;
let seconds = t;
timerS.innerHTML = String(hours).padStart(2, "0") + ":"
+ String(minutes).padStart(2, "0") + ":"
+ String(seconds).padStart(2, "0");
}
function populate_infobox(test_data) {
if (INTERVAL_HANDLE !== null) {
clearInterval(INTERVAL_HANDLE);
}
let test_concluded = TEST_DATA["state"] === "concluded";
let game_nameS = document.getElementById("game_name");
let durationS = document.getElementById("duration");
let percentageS = document.getElementById("percentage");
game_nameS.innerHTML = test_data["gamename"];
if (test_concluded) {
let summary = test_data["summary"];
let correct_answer_n = summary["correct_answer_n"];
let challenge_n = summary["challenge_n"];
let r = Math.ceil((correct_answer_n / challenge_n) * 100);
percentageS.innerHTML = `${r}% (${correct_answer_n}/${challenge_n})`;
let start_time = unix_time_to_human_readable(test_data["start_time"]);
let end_time = unix_time_to_human_readable(test_data["end_time"]);
durationS.value = `${start_time} - ${end_time}`;
hide("ongoing-info");
show("concluded-info");
} else {
if (test_data["time_limited"]) {
let time_left_s = Number(test_data["end_limit_time"]) - Number(test_data["current_time"]);
print_time_left(time_left_s);
INTERVAL_HANDLE = setInterval(() => {
time_left_s--;
print_time_left(time_left_s);
if (time_left_s <= 0) {
populate_all(test_data["_id"]);
clearInterval(INTERVAL_HANDLE);
INTERVAL_HANDLE = null;
}
}, 1000);
}
show("ongoing-info");
hide("concluded-info");
}
}
function populate_challenges(test_data) {
let test_display = document.getElementById("test_display");
test_display.innerHTML = "";
let test_concluded = TEST_DATA["state"] === "concluded";
let challenge_N = 0;
test_data["challenges"].forEach((challenge) => {
let challenge_N_snapshot = challenge_N;
let challenge_box = document.createElement("section");
challenge_box.classList.add("challenge");
let question = document.createElement("span");
question.classList.add("question");
question.innerHTML = preprocess_inserts(challenge["question"]);
let answer_container = document.createElement("section");
answer_container.classList.add("answer-container");
challenge_box.append(question, answer_container);
let answer_N = 0;
let player_answer = challenge["player_answer"];
player_answer = (player_answer !== "") ? Number(player_answer) : -1;
challenge["answers"].forEach((answer) => {
let answer_section = document.createElement("section");
answer_section.classList.add("answer");
let answer_radio = document.createElement("input");
answer_radio.type = "radio";
answer_radio.id = `${challenge_N}_${answer_N}`;
answer_radio.name = `challenge_${challenge_N}`;
answer_radio.disabled = test_concluded;
let answer_N_snapshot = answer_N;
answer_radio.addEventListener("input", () => {
save_answer(challenge_N_snapshot, answer_N_snapshot);
});
if (player_answer === answer_N) {
answer_radio.checked = true;
}
let answer_text = document.createElement("label");
answer_text.innerHTML = preprocess_inserts(answer);
answer_text.setAttribute("for", answer_radio.id);
if (test_concluded && (challenge["correct_answer"] === answer_N)) {
answer_text.classList.add("correct-answer")
}
answer_section.append(answer_radio, answer_text);
answer_container.appendChild(answer_section);
answer_N++;
});
challenge_N++;
test_display.appendChild(challenge_box);
});
MathJax.typeset();
}
function populate_all(test_id) {
let req = {
action: "get_test",
id: test_id
testid: test_id
}
request(req).then(resp => {
let test_data = JSON.parse(resp);
test_data["challenges"].forEach((challenge) => {
let challenge_box = document.createElement("section");
challenge_box.classList.add("challenge");
let question = document.createElement("span");
question.classList.add("question");
question.innerHTML = challenge["question"];
let answer_container = document.createElement("section");
answer_container.classList.add("answer-container");
challenge_box.append(question, answer_container);
challenge["answers"].forEach((answer) => {
let answer_section = document.createElement("section");
answer_section.classList.add("answer");
answer_section.innerHTML = answer;
answer_container.appendChild(answer_section);
});
test_display.appendChild(challenge_box);
});
TEST_DATA = JSON.parse(resp);
populate_challenges(TEST_DATA);
populate_infobox(TEST_DATA);
});
}
function save_answer(chidx, aidx) {
let req = {
action: "save_answer",
testid: TEST_DATA["_id"],
challenge_index: chidx,
answer_index: aidx,
};
request(req);
}
function preprocess_inserts(str) {
let code_delim = '`';
let parts = str.split(code_delim);
let res = "";
for (let i = 0; i < parts.length; i++) {
res += parts[i];
if (i % 2 === 0) {
res += "<code>";
} else {
res += "</code>";
}
}
return res;
}
function submit_test() {
let req = {
action: "submit_test",
testid: TEST_DATA["_id"]
}
request(req).then(resp => {
populate_all(TEST_DATA["_id"]);
});
}

View File

@ -22,6 +22,7 @@ $privilege = $user_data["privilege"];
<meta charset="UTF-8">
<title>SpreadQuiz</title>
<script src="js/req.js"></script>
<script src="js/main.js"></script>
<script src="js/spreadquiz.js"></script>
<link rel="stylesheet" href="style/spreadquiz.css"/>
</head>
@ -31,7 +32,13 @@ $privilege = $user_data["privilege"];
<iframe id="content_frame" src="default_frame.php"></iframe>
</section>
<section id="info_pane">
<section id="user_info" class="info-pane-element"><?= $user_data["nickname"]; ?></section>
<section id="user_info" class="info-pane-element">
<?= $user_data["nickname"]; ?>
<section style="margin-top: 1em">
<input type="button" value="Beállítások"><br>
<input type="button" value="Kijelentkezés" onclick="logout()">
</section>
</section>
<?php if ($privilege != PRIVILEGE_PLAYER) { ?>
<section id="action_panel" class="info-pane-element">
<?php if (($privilege === PRIVILEGE_CREATOR) || ($privilege === PRIVILEGE_QUIZMASTER)) { ?>

45
style/quizmaster_area.css Normal file
View File

@ -0,0 +1,45 @@
section#table_section {
position: sticky;
height: calc(100vh - 4em);
overflow-y: auto;
}
table.management {
width: 100%;
border-collapse: collapse;
}
table.management thead th {
padding: 0.5em 0;
position: sticky;
top: 0;
background-color: darkgray;
}
table.management tbody td {
border: 1pt lightgrey solid;
padding: 0.3em 0.5em;
}
table.management tbody tr[highlight="true"] {
background-color: antiquewhite;
}
table.management tbody tr[highlight="false"] td input {
pointer-events: none;
}
section#user_manager_action_bar {
margin-top: 0.5em;
}
td.checkbox {
width: 0;
}
.hintbox-window {
position: fixed;
display: block;
width: 15em;
height: 8em;
}

View File

@ -1,3 +1,10 @@
@import url('https://fonts.googleapis.com/css2?family=Autour+One&family=Kanit:wght@500&display=swap');
@import url("https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0");
body {
font-family: 'Autour One', sans-serif;
}
*[shown="false"] {
visibility: hidden;
display: none;
@ -10,6 +17,10 @@
opacity: 1;
}
.material {
font-family: 'Material Symbols Outlined', sans-serif;
}
/* ----------------- */
section#screen_panel {
@ -31,13 +42,15 @@ section#content_pane, section#info_pane {
section#content_pane {
left: 0;
width: 80vw;
background-color: lightcyan;
/*background-color: lightcyan;*/
}
section#info_pane {
right: 0;
width: 20vw;
background-color: beige;
background-color: whitesmoke;
border-left: 5px solid #176767;
box-shadow: -5px 0 #d3e5e5;
}
.info-pane-element {
@ -50,9 +63,19 @@ section#info_pane {
section#user_info {
position: relative;
top: 0;
background-color: aquamarine;
background-color: #176767;
color: whitesmoke;
font-size: 18pt;
text-align: center;
height: 1.2em;
overflow: clip;
transition: 0.3s ease;
border-bottom: 0 dashed whitesmoke;
}
section#user_info:hover {
height: 5em;
border-width: 0.5em;
}
section#action_panel {
@ -61,46 +84,11 @@ section#action_panel {
iframe#content_frame {
display: block;
width: 100%;
width: calc(100% - 1em);
height: 100%;
border: 0 transparent;
}
section#table_section {
position: sticky;
height: calc(100vh - 4em);
overflow-y: auto;
}
table.management {
width: 100%;
border-collapse: collapse;
}
table.management thead th {
padding: 0.5em 0;
position: sticky;
top: 0;
background-color: darkgray;
}
table.management tbody td {
border: 1pt lightgrey solid;
padding: 0.3em 0.5em;
}
table.management tbody tr[highlight="true"] {
background-color: antiquewhite;
}
table.management tbody tr[highlight="false"] td input {
pointer-events: none;
}
section#user_manager_action_bar {
margin-top: 0.5em;
}
section.window {
position: fixed;
display: block;
@ -115,23 +103,12 @@ section.window {
section.window-inner {
margin: 0 auto;
display: block;
border: 2pt solid darkslategray;
border: 2pt solid #176767;
background-color: whitesmoke;
padding: 1em;
width: fit-content;
}
td.checkbox {
width: 0;
}
.hintbox-window {
position: fixed;
display: block;
width: 15em;
height: 8em;
}
/* ----- */
section#game_list_panel {
@ -145,15 +122,30 @@ section.group-box {
margin: 1em;
left: 0;
right: 0;
background-color: beige;
background-color: #f8fbfb;
border: 0.5px solid #176767;
/*border-radius: 0 0 0.2em 0.2em;*/
box-shadow: 5px 5px #d3e5e5;
}
span.group-box-caption {
span.group-box-caption-container {
display: block;
left: 0;
right: 0;
background-color: #176767;
box-shadow: 0 5px #d3e5e5;
}
span.group-box-caption {
display: inline-block;
/*position: relative;*/
/*top: -0.5em;*/
color: #176767;
background-color: #d3e5e5;
font-weight: bold;
font-size: 1.2em;
padding: 1em;
background-color: chartreuse;
margin-left: 1em;
}
section.group-box-inner {
@ -164,13 +156,45 @@ section.group-box-inner {
}
section.game-box {
display: table-cell;
width: 8em;
height: 8em;
border: 2pt dashed gray;
display: inline-block;
position: relative;
width: 12em;
height: 12em;
border: 2pt solid #176767;
text-align: center;
vertical-align: middle;
cursor: pointer;
margin-right: 1em;
overflow: clip;
box-shadow: 5px 5px #d3e5e5;
transform: scale(100%) rotate(0);
background-color: whitesmoke;
transition: ease 0.3s;
}
section.game-box:hover {
transform: scale(105%) rotate(3deg);
z-index: 1000;
}
section.game-box-caption {
margin-top: 2em;
font-size: 1.3em;
rotate: -10deg;
background-color: #176767;
width: 13em;
position: relative;
left: -3em;
padding: 0.6em;
color: whitesmoke;
box-shadow: 0 5px #d3e5e5;
}
section.list-results-btn {
display: block;
position: absolute;
right: 0.2em;
bottom: 0.2em;
}
section#test_area {
@ -181,11 +205,20 @@ section#test_area {
}
section.challenge {
display: block;
margin: 0 auto;
width: 40em;
/*border: 1px solid black;*/
padding: 1em;
background-color: #d3e5e5;
margin-bottom: 0.5em;
border-radius: 0.3em;
}
span.question {
font-size: 1.3em;
font-weight: bold;
color: #176767;
}
section.answer-container {
@ -193,5 +226,112 @@ section.answer-container {
}
section.answer {
margin: 0.3em 0.8em;
display: block;
}
section.answer label {
margin-left: 0.5em;
padding: 0.3em 0.5em;
border-radius: 0.3em;
}
section.answer label.correct-answer {
border: 2px solid #176767 !important;
padding: 0.1em;
}
section.answer input[type="radio"]:checked+label:not(.correct-answer) {
background-color: #176767;
color: whitesmoke;
}
section#infobox {
display: block;
position: fixed;
left: 0;
top: 0;
margin: 1em;
/*padding: 1em;*/
background: #f8fbfb;
border: 0.5px solid #176767;
min-width: 15em;
min-height: 5em;
box-shadow: 5px 5px #d3e5e5;
overflow: clip;
}
section#infobox > section {
padding: 1em;
}
span#game_name {
display: block;
width: 100%;
padding: 1em;
background-color: #176767;
box-shadow: 0px 5px #d3e5e5;
color: whitesmoke;
font-size: 1.2em;
font-weight: bold;
}
section#timer {
color: #176767;
margin-bottom: 0.5em;
}
section#percentage {
color: #176767;
}
span.infobox-description {
font-size: 0.8em;
}
.btn {
background-color: transparent;
padding: 0.3em;
border-radius: 0.2em;
font-size: 1.3em;
}
.btn:hover {
background-color: rgba(128, 128, 128, 0.30);
}
section.test-summary-record {
position: relative;
cursor: pointer;
border: 0.5px solid #176767;
margin-bottom: 0.3em;
}
.summary-sequence-number {
font-size: 2em;
display: inline-block;
background-color: #176767;
color: whitesmoke;
padding: 0.5em;
width: 1.5em;
box-shadow: 5px 0 #d3e5e5;
}
.summary-duration {
display: inline-block;
margin-left: 1em;
color: #176767;
font-size: 0.7em;
}
.summary-percentage {
display: inline-block;
position: absolute;
right: 0;
margin-left: 1em;
background-color: #176767;
padding: 0 1em;
box-shadow: -5px 0 #d3e5e5;
color: whitesmoke;
bottom: 0;
}

View File

@ -8,7 +8,10 @@ if (!get_autologin_state() || !isset($_REQUEST["testid"])) {
exit();
}
$testid = $_REQUEST["testid"];
$testid = trim($_REQUEST["testid"] ?: "");
if ($testid === "") {
exit();
}
?>
@ -17,15 +20,35 @@ $testid = $_REQUEST["testid"];
<head>
<meta charset="UTF-8">
<title>SpreadQuiz</title>
<script src="js/spreadquiz.js"></script>
<script src="js/req.js"></script>
<script src="js/spreadquiz.js"></script>
<script src="js/o.js"></script>
<script src="js/common.js"></script>
<script src="js/testground.js"></script>
<link rel="stylesheet" href="style/spreadquiz.css">
<script id="MathJax-script" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
</head>
<body>
<section id="test_display">
<section id="test_display">
</section>
<section id="infobox">
<span id="game_name"></span>
<section>
<section id="ongoing-info">
<span class="infobox-description">Hátralevő idő:</span>
<section id="timer">10:00:00</section>
<input type="button" value="Beküld" onclick="submit_test()">
</section>
<section id="concluded-info">
<section id="duration"></section>
<span class="infobox-description">Eredmény:</span>
<section id="percentage">95% (19/20)</section>
</section>
</section>
</section>
<script>
populate_all("<?=$testid ?>");
</script>
</body>
</html>

View File

@ -29,7 +29,7 @@ function create_or_continue_test(string $gameid, string $nickname): string
}
// check if the user had taken this test before
$fetch_criteria = [["gameid", "=", $gameid], "AND", ["nickname", "=", $nickname]];
$fetch_criteria = [["gameid", "=", (int)$gameid], "AND", ["nickname", "=", $nickname]];
$previous_tests = $testdb->findBy($fetch_criteria);
if (count($previous_tests) > 0) { // if there are previous attempts, then...
// update timed tests to see if they had expired
@ -53,12 +53,15 @@ function create_or_continue_test(string $gameid, string $nickname): string
function create_test(array $game_data, array $user_data) : string
{
global $testdb;
$gameid = $game_data["_id"];
$game_properties = $game_data["properties"];
// fill basic data
$game_data = [
$test_data = [
"gameid" => $gameid,
"nickname" => $user_data["nickname"],
"gamename" => $game_data["name"]
];
// fill challenges
@ -67,11 +70,33 @@ function create_test(array $game_data, array $user_data) : string
// shuffle answers
foreach ($challenges as &$ch) {
shuffle($ch["answers"]);
$ch["correct_answer"] = "";
$ch["player_answer"] = "";
}
$game_data["challenges"] = $challenges;
// involve properties
$now = time();
$properties = [
"state" => TEST_ONGOING,
"time_limited" => (($game_properties["time_limit"] ?: -1) > -1),
"start_time" => $now,
"repeatable" => $game_properties["repeatable"] ?: false
];
if ($properties["time_limited"]) {
$properties["end_limit_time"] = $now + $game_properties["time_limit"];
}
// merge properties and test data
$test_data = array_merge($test_data, $properties);
// add challenges
$test_data["challenges"] = $challenges;
// store game
$test_data = $testdb->insert($test_data);
$testid = $test_data["_id"];
return $testid;
}
function update_timed_tests(array $test_data_array)
@ -79,9 +104,8 @@ function update_timed_tests(array $test_data_array)
$now = time();
foreach ($test_data_array as $test_data) {
// look for unprocessed expired tests
if (($test_data["state"] === TEST_ONGOING) && ($test_data["end_limit_time"] < $now)) {
$test_data["state"] = TEST_CONCLUDED;
update_test($test_data);
if (($test_data["state"] === TEST_ONGOING) && ($test_data["time_limited"]) && ($test_data["end_limit_time"] < $now)) {
conclude_test($test_data["_id"]);
}
}
}
@ -91,3 +115,68 @@ function update_test(array $test_data)
global $testdb;
$testdb->update($test_data);
}
function get_test(string $testid): array
{
global $testdb;
return $testdb->findById($testid);
}
function save_answer(string $testid, $chidx, $ansidx)
{
$test_data = get_test($testid);
$chidx = (int)$chidx;
if ((count($test_data) > 0) && ($test_data["state"] === TEST_ONGOING)) {
if ($chidx < count($test_data["challenges"])) {
$test_data["challenges"][$chidx]["player_answer"] = $ansidx;
update_test($test_data);
}
}
}
function get_concluded_tests(string $gameid, string $nickname) {
global $testdb;
$fetch_criteria = [["gameid", "=", (int)$gameid], "AND", ["nickname", "=", $nickname], "AND", ["state", "=", TEST_CONCLUDED]];
$test_data_array = $testdb->findBy($fetch_criteria);
return $test_data_array;
}
function conclude_test(string $testid) {
$test_data = get_test($testid);
if (count($test_data) === 0) {
return;
}
// load game data
//$game_data = get_game($test_data["gameid"]);
$game_challenges = load_challenges($test_data["gameid"]);
// check the answers
$challenge_n = count($test_data["challenges"]); // number of challenges
$cans_n = 0; // number of correct answers
for ($chidx = 0; $chidx < $challenge_n; $chidx++) {
// get challenge
$tch = &$test_data["challenges"][$chidx];
$gch = $game_challenges[$chidx];
// translate correct answer into an index by the shuffled answer order
$cans_idx = array_search($gch["correct_answer"], $tch["answers"]);
$tch["correct_answer"] = $cans_idx;
// check the player's answer
$player_answer = trim($tch["player_answer"]);
if (($player_answer !== "") && ($cans_idx === (int)$player_answer)) {
$cans_n++;
}
}
// set state and fill summary
$test_data["state"] = TEST_CONCLUDED;
$test_data["end_time"] = time();
$test_data["summary"] = [
"challenge_n" => $challenge_n,
"correct_answer_n" => $cans_n
];
update_test($test_data);
}