diff --git a/game_manager_frame.php b/game_manager_frame.php index 3fea850..460cffd 100644 --- a/game_manager_frame.php +++ b/game_manager_frame.php @@ -92,6 +92,14 @@ if (!get_autologin_state() || (($user_data["privilege"] !== PRIVILEGE_CREATOR) & + + + + + + + + @@ -101,20 +109,13 @@ if (!get_autologin_state() || (($user_data["privilege"] !== PRIVILEGE_CREATOR) & - - - - - - - - Eredmények: - + + diff --git a/gamemgr.php b/gamemgr.php index 4e1c720..5a41b9b 100644 --- a/gamemgr.php +++ b/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; diff --git a/interface.php b/interface.php index 977038c..b6aa0de 100644 --- a/interface.php +++ b/interface.php @@ -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; diff --git a/js/gamemgr.js b/js/gamemgr.js index f277da8..44f2667 100644 --- a/js/gamemgr.js +++ b/js/gamemgr.js @@ -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"); diff --git a/js/result_analyzer.js b/js/result_analyzer.js new file mode 100644 index 0000000..b63695b --- /dev/null +++ b/js/result_analyzer.js @@ -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); + }); + + }); +} diff --git a/login.php b/login.php index e30e016..1a7fe1b 100644 --- a/login.php +++ b/login.php @@ -28,5 +28,15 @@ if (get_autologin_state()) { + diff --git a/result_analyzer.php b/result_analyzer.php new file mode 100644 index 0000000..bca0a6a --- /dev/null +++ b/result_analyzer.php @@ -0,0 +1,79 @@ + + + + + + + + SpreadQuiz - Eredménykezelő + + + + + + + + + + +
+ + +
+
+
+ + + + + + + + + + + +
+ Felhasználónév
+ + [nickname] + +
Eredmény
+ + [result] + +
Időbélyeg
+ + [timestamp] + +
+
+
+ + + diff --git a/testground.php b/testground.php index 1552fb8..631486a 100644 --- a/testground.php +++ b/testground.php @@ -9,10 +9,12 @@ if (!get_autologin_state() || !isset($_REQUEST["testid"])) { } $testid = trim($_REQUEST["testid"] ?: ""); + if ($testid === "") { exit(); } + ?> diff --git a/testmgr.php b/testmgr.php index 6f21673..1530de6 100644 --- a/testmgr.php +++ b/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; } \ No newline at end of file