- automatic checking of publishing conditions
- result analyzer with basic functionality added
This commit is contained in:
parent
2fd08094a6
commit
fa74f65847
@ -92,6 +92,14 @@ if (!get_autologin_state() || (($user_data["privilege"] !== PRIVILEGE_CREATOR) &
|
||||
<input type="text" pattern="(([01][0-9])|([2][0-3])):[0-5][0-9]:[0-5][0-9]" id="time_limit">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<label for="repeatable">Megismételhető:</label>
|
||||
</td>
|
||||
<td>
|
||||
<input type="checkbox" id="repeatable">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<label for="public">Publikus:</label>
|
||||
@ -101,20 +109,13 @@ if (!get_autologin_state() || (($user_data["privilege"] !== PRIVILEGE_CREATOR) &
|
||||
<input type="text" id="public_url" readonly>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<label for="repeatable">Megismételhető:</label>
|
||||
</td>
|
||||
<td>
|
||||
<input type="checkbox" id="repeatable">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Eredmények:
|
||||
</td>
|
||||
<td>
|
||||
<input type="button" value="Mutat" onclick="list_results_by_game(EDITED_GAME)">
|
||||
<input type="button" value="Gyors áttekintés" onclick="list_results_by_game(EDITED_GAME)">
|
||||
<input type="button" value="Részletes megjelenítés" onclick="window.open(`result_analyzer.php?game_id=${EDITED_GAME._id}`)">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
34
gamemgr.php
34
gamemgr.php
@ -11,6 +11,13 @@ const DEFAULT_GAME_PROPERTIES = [
|
||||
"repeatable" => false // this test can be taken multiple times
|
||||
];
|
||||
|
||||
const CURRENT_GAME_VERSION = 2; // MUST BE INCREMENTED!!
|
||||
|
||||
function generate_public_id(): string
|
||||
{
|
||||
return uniqid("p");
|
||||
}
|
||||
|
||||
function create_game(string $name, string $owner, string $description = "", array $properties = DEFAULT_GAME_PROPERTIES, array $contributors = [], array $challenges = []): bool
|
||||
{
|
||||
global $gamedb;
|
||||
@ -23,7 +30,8 @@ function create_game(string $name, string $owner, string $description = "", arra
|
||||
"properties" => $properties,
|
||||
"groups" => [],
|
||||
"public" => false,
|
||||
"public_id" => uniqid("p"),
|
||||
"public_id" => generate_public_id(),
|
||||
"version" => CURRENT_GAME_VERSION
|
||||
];
|
||||
$game_data = $gamedb->insert($game_data);
|
||||
|
||||
@ -41,6 +49,12 @@ function get_game(string $gameid): array
|
||||
return $gamedb->findById($gameid);
|
||||
}
|
||||
|
||||
function get_public_game(string $public_id): array
|
||||
{
|
||||
global $gamedb;
|
||||
return $gamedb->findBy([["public", "=", "true"], "AND", ["public_id", "=", $public_id]]);
|
||||
}
|
||||
|
||||
function update_game(array $game_data)
|
||||
{
|
||||
global $gamedb;
|
||||
@ -74,6 +88,21 @@ function get_all_games()
|
||||
return $gamedb->findAll();
|
||||
}
|
||||
|
||||
function patch_up_game_data(array &$game_data)
|
||||
{
|
||||
$game_version = $game_data["version"] ?: 0;
|
||||
if ($game_version < 2) { // update to game version 2
|
||||
if (!key_exists("public_id", $game_data)) {
|
||||
$game_data["public"] = false;
|
||||
$game_data["public_id"] = generate_public_id();
|
||||
}
|
||||
|
||||
$game_data["version"] = 2;
|
||||
}
|
||||
|
||||
return $game_version < CURRENT_GAME_VERSION;
|
||||
}
|
||||
|
||||
function get_all_game_data_by_contributor_nickname(string $nickname): array
|
||||
{
|
||||
global $gamedb;
|
||||
@ -84,6 +113,9 @@ function get_all_game_data_by_contributor_nickname(string $nickname): array
|
||||
$game_data_array = $gamedb->findAll();
|
||||
}
|
||||
foreach ($game_data_array as $game_data) {
|
||||
if (patch_up_game_data($game_data)) {
|
||||
update_game($game_data);
|
||||
}
|
||||
$game_headers[] = $game_data;
|
||||
}
|
||||
return $game_headers;
|
||||
|
@ -221,7 +221,14 @@ switch ($action) {
|
||||
$game_data["properties"]["time_limit"] = $properties["time_limit"];
|
||||
$game_data["properties"]["repeatable"] = $properties["repeatable"];
|
||||
|
||||
$game_data["public"] = $data["public"];
|
||||
// process game public flag: a game might be only public if not being time-constrained and is allowed to be taken multiple times
|
||||
if (($properties["time_limit"] !== 0) || (!$properties["repeatable"])) {
|
||||
$game_data["public"] = false;
|
||||
} else {
|
||||
$game_data["public"] = $data["public"];
|
||||
}
|
||||
|
||||
// update game data
|
||||
update_game($game_data);
|
||||
|
||||
// update game file if supplied
|
||||
@ -305,8 +312,10 @@ switch ($action) {
|
||||
case "get_results_by_gameid":
|
||||
{
|
||||
$gameid = trim($_REQUEST["gameid"] ?: "");
|
||||
$filter = trim($_REQUEST["filter"] ?: "");
|
||||
if (($gameid !== "") && (is_user_contributor_to_game($gameid, $nickname) || $is_quizmaster)) {
|
||||
$result = json_encode(get_results_by_gameid($gameid));
|
||||
$game_results = get_results_by_gameid($gameid, $filter);
|
||||
$result = json_encode($game_results);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -58,6 +58,20 @@ function create_edit_game(game = null) {
|
||||
let publicChk = document.getElementById("public");
|
||||
let publicUrlF = document.getElementById("public_url");
|
||||
|
||||
let hide_public_url_field = () => {
|
||||
publicUrlF.hidden = !publicChk.checked;
|
||||
}
|
||||
|
||||
let hide_public_option = () => {
|
||||
let hide = (!repeatableChk.checked) || (time_limitedChk.checked);
|
||||
if (hide) {
|
||||
publicChk.checked = false;
|
||||
}
|
||||
publicChk.disabled = hide;
|
||||
|
||||
hide_public_url_field();
|
||||
}
|
||||
|
||||
if (!updating) { // creating a new game
|
||||
nameF.value = "";
|
||||
descriptionF.value = "";
|
||||
@ -82,9 +96,7 @@ function create_edit_game(game = null) {
|
||||
contributorsF.value = game["contributors"].join(", ");
|
||||
groupF.value = game["groups"].join(", ");
|
||||
|
||||
publicChk.addEventListener("change", () => {
|
||||
publicUrlF.hidden = !publicChk.checked;
|
||||
});
|
||||
publicChk.addEventListener("change", hide_public_url_field);
|
||||
publicChk.checked = game["public"];
|
||||
publicUrlF.hidden = !publicChk.checked;
|
||||
publicUrlF.value = game["public_id"];
|
||||
@ -93,9 +105,13 @@ function create_edit_game(game = null) {
|
||||
let time_limit = Math.max(Number(props["time_limit"]), 0);
|
||||
time_limitF.value = seconds_to_time(time_limit);
|
||||
time_limitedChk.checked = time_limit > 0;
|
||||
time_limitedChk.addEventListener("change", hide_public_option);
|
||||
handle_time_limit_chkbox();
|
||||
|
||||
repeatableChk.checked = props["repeatable"];
|
||||
repeatableChk.addEventListener("change", hide_public_option);
|
||||
|
||||
hide_public_option();
|
||||
|
||||
// show additional controls
|
||||
show("additional_controls");
|
||||
|
58
js/result_analyzer.js
Normal file
58
js/result_analyzer.js
Normal file
@ -0,0 +1,58 @@
|
||||
function create_cell(content) {
|
||||
let cell = document.createElement("td");
|
||||
cell.innerHTML = content;
|
||||
return cell;
|
||||
}
|
||||
|
||||
function fetch_results() {
|
||||
|
||||
let filterF = document.getElementById("filter");
|
||||
|
||||
let req = {action: "get_results_by_gameid", gameid: GAMEID, filter: filterF.value.trim()};
|
||||
|
||||
request(req).then(resp => {
|
||||
let rd = document.getElementById("results_display");
|
||||
|
||||
let results = JSON.parse(resp);
|
||||
let empty_resp = results.length === 0;
|
||||
rd.innerHTML = empty_resp ? "Nincs találat." : "";
|
||||
if (empty_resp) {
|
||||
return;
|
||||
}
|
||||
|
||||
// let n = results.length;
|
||||
|
||||
results.forEach((record) => {
|
||||
let row = document.createElement("tr");
|
||||
|
||||
// is the game concluded
|
||||
let concluded = record["state"] === "concluded";
|
||||
|
||||
let percentage = "-";
|
||||
let timestamp = "-";
|
||||
|
||||
// replace some fields if game was concluded
|
||||
if (concluded) {
|
||||
// percentage
|
||||
let summary = record["summary"];
|
||||
let r = Math.floor((summary["correct_answer_n"] / summary["challenge_n"]) * 100);
|
||||
percentage = `${r}%`;
|
||||
|
||||
// finish timestamp
|
||||
timestamp = unix_time_to_human_readable(record["end_time"]);
|
||||
}
|
||||
|
||||
// create cells
|
||||
let empty_cell = create_cell("");
|
||||
let name_cell = create_cell(record.nickname)
|
||||
let percentage_cell = create_cell(percentage)
|
||||
let timestamp_cell = create_cell(timestamp);
|
||||
|
||||
row.append(empty_cell, name_cell, percentage_cell, timestamp_cell);
|
||||
|
||||
// append row
|
||||
rd.appendChild(row);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
10
login.php
10
login.php
@ -28,5 +28,15 @@ if (get_autologin_state()) {
|
||||
<input type="button" value="Belépés" onclick="login()">
|
||||
</section>
|
||||
</section>
|
||||
<script>
|
||||
function login_at_pressing_enter(evt) {
|
||||
if (evt.key === "Enter") {
|
||||
login();
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById("nickname").addEventListener("keydown", login_at_pressing_enter);
|
||||
document.getElementById("password").addEventListener("keydown", login_at_pressing_enter);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
79
result_analyzer.php
Normal file
79
result_analyzer.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
require_once "globals.php";
|
||||
require_once "autologin.php";
|
||||
|
||||
$user_data = get_autologin_user_data();
|
||||
if (!get_autologin_state()) {
|
||||
exit();
|
||||
}
|
||||
|
||||
// fetch game ID; cannot continue without specifying it
|
||||
$game_id = trim($_REQUEST["game_id"] ?: "");
|
||||
if ($game_id === "") {
|
||||
exit();
|
||||
}
|
||||
|
||||
require_once "gamemgr.php";
|
||||
|
||||
// no user without access may tamper with the results
|
||||
if (!is_user_contributor_to_game($game_id, $user_data["nickname"])) {
|
||||
exit();
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0'>
|
||||
<title>SpreadQuiz - Eredménykezelő</title>
|
||||
<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/result_analyzer.js"></script>
|
||||
<link rel="stylesheet" href="style/spreadquiz.css">
|
||||
<link rel="stylesheet" href="style/quizmaster_area.css"/>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<section style="margin-bottom: 0.3em">
|
||||
<input type="text" placeholder="Szűrőfeltétel" id="filter" style="font-family: 'Monaco', monospace; width: 50em;">
|
||||
<input type="button" value="Szűrés" onclick="fetch_results()">
|
||||
</section>
|
||||
<section>
|
||||
<section id="table_section">
|
||||
<table class="management">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>
|
||||
Felhasználónév<br>
|
||||
<code>
|
||||
[nickname]
|
||||
</code>
|
||||
</th>
|
||||
<th>Eredmény<br>
|
||||
<code>
|
||||
[result]
|
||||
</code>
|
||||
</th>
|
||||
<th>Időbélyeg<br>
|
||||
<code>
|
||||
[timestamp]
|
||||
</code>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="results_display">
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
</section>
|
||||
<script>
|
||||
let GAMEID = <?="$game_id"?>;
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
@ -9,10 +9,12 @@ if (!get_autologin_state() || !isset($_REQUEST["testid"])) {
|
||||
}
|
||||
|
||||
$testid = trim($_REQUEST["testid"] ?: "");
|
||||
|
||||
if ($testid === "") {
|
||||
exit();
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
|
109
testmgr.php
109
testmgr.php
@ -51,7 +51,7 @@ function create_or_continue_test(string $gameid, string $nickname): string
|
||||
}
|
||||
}
|
||||
|
||||
function create_test(array $game_data, array $user_data) : string
|
||||
function create_test(array $game_data, array $user_data): string
|
||||
{
|
||||
global $testdb;
|
||||
$gameid = $game_data["_id"];
|
||||
@ -134,14 +134,16 @@ function save_answer(string $testid, $chidx, $ansidx)
|
||||
}
|
||||
}
|
||||
|
||||
function get_concluded_tests(string $gameid, string $nickname) {
|
||||
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) {
|
||||
function conclude_test(string $testid)
|
||||
{
|
||||
$test_data = get_test($testid);
|
||||
if (count($test_data) === 0) {
|
||||
return;
|
||||
@ -181,9 +183,104 @@ function conclude_test(string $testid) {
|
||||
update_test($test_data);
|
||||
}
|
||||
|
||||
function get_results_by_gameid(string $gameid) : array {
|
||||
function split_criterion(string $crstr): array {
|
||||
preg_match("/([<>=!]+|LIKE|NOT LIKE|IN|NOT IN|CONTAINS|NOT CONTAINS|BETWEEN|NOT BETWEEN|EXISTS)/", $crstr, $matches, PREG_OFFSET_CAPTURE);
|
||||
|
||||
// extract operator
|
||||
$op = $matches[0][0];
|
||||
$op_pos = $matches[0][1];
|
||||
|
||||
// extract operands
|
||||
$left = trim(substr($crstr, 0, $op_pos));
|
||||
$right = trim(substr($crstr, $op_pos + strlen($op), strlen($crstr)));
|
||||
|
||||
// automatic type conversion
|
||||
if (($right[0] !== "\"") && ($right[0] !== "\'")) { // numeric value
|
||||
$right = (int) $right;
|
||||
} else { // string value
|
||||
$right = substr($right, 1, strlen($right) - 2); // strip leading and trailing quotes
|
||||
}
|
||||
|
||||
return [$left, $op, $right];
|
||||
}
|
||||
|
||||
function build_query(string $filter): array
|
||||
{
|
||||
// skip empty filter processing
|
||||
if (trim($filter) === "") {
|
||||
return [];
|
||||
}
|
||||
|
||||
// subfilters and operations
|
||||
$subfilts = [];
|
||||
$operations = [];
|
||||
|
||||
// buffer and scoring
|
||||
$k = 0;
|
||||
$k_prev = 0;
|
||||
$buffer = "";
|
||||
|
||||
for ($i = 0; $i < strlen($filter); $i++) {
|
||||
$c = $filter[$i];
|
||||
|
||||
// extract groups surrounded by parantheses
|
||||
if ($c === "(") {
|
||||
$k++;
|
||||
} elseif ($c === ")") {
|
||||
$k--;
|
||||
} else {
|
||||
$buffer .= $c;
|
||||
}
|
||||
|
||||
// if k = 0, then we found a subfilter
|
||||
if (($k === 0) && ($k_prev === 1)) {
|
||||
$subfilts[] = trim($buffer);
|
||||
$buffer = "";
|
||||
} elseif (($k === 1) && ($k_prev === 0)) {
|
||||
$op = trim($buffer);
|
||||
if ($op !== "") {
|
||||
$operations[] = $op;
|
||||
}
|
||||
$buffer = "";
|
||||
}
|
||||
|
||||
// save k to be used next iteration
|
||||
$k_prev = $k;
|
||||
}
|
||||
|
||||
// decide, whether further expansion of condition is needed
|
||||
$criteria = [];
|
||||
for ($i = 0; $i < count($subfilts); $i++) {
|
||||
$subfilt = $subfilts[$i];
|
||||
|
||||
// add subcriterion
|
||||
if ($subfilt[0] === "(") {
|
||||
$criteria[] = build_query($subfilt);
|
||||
} else {
|
||||
$criteria[] = split_criterion($subfilt);
|
||||
}
|
||||
|
||||
// add operator
|
||||
if (($i + 1) < count($subfilts)) {
|
||||
$criteria[] = $operations[$i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return $criteria;
|
||||
}
|
||||
|
||||
function get_results_by_gameid(string $gameid, string $filter): array
|
||||
{
|
||||
global $testdb;
|
||||
$fetch_criteria = ["gameid", "=", (int)$gameid];
|
||||
$test_data_array = $testdb->findBy($fetch_criteria);
|
||||
$qb = $testdb->createQueryBuilder();
|
||||
$qb = $qb->where(["gameid", "=", (int)$gameid]);
|
||||
|
||||
if (trim($filter) !== "") {
|
||||
$criteria = build_query($filter);
|
||||
$qb->where($criteria);
|
||||
}
|
||||
|
||||
$test_data_array = $qb->getQuery()->fetch();
|
||||
return $test_data_array;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user