- report generation implemented
- recursive filtering fixed - grouping added
This commit is contained in:
		
							parent
							
								
									55a68138b5
								
							
						
					
					
						commit
						89ac41a6e6
					
				@ -112,9 +112,9 @@ switch ($action) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// test-related queries
 | 
					// test-related queries
 | 
				
			||||||
if ((($testid = trim($_REQUEST["testid"] ?: "")) !== "") &&
 | 
					if (isset($_REQUEST["testid"]) && (($testid = trim($_REQUEST["testid"])) !== "") &&
 | 
				
			||||||
    ((count($test_data = get_test($testid))) > 0) &&
 | 
					    ((count($test_data = get_test($testid))) > 0) &&
 | 
				
			||||||
    ($test_data["nickname"] === $nickname)) {
 | 
					    (($test_data["nickname"] === $nickname) || $is_quizmaster)) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // update the test if timed
 | 
					    // update the test if timed
 | 
				
			||||||
    update_timed_tests([$test_data]);
 | 
					    update_timed_tests([$test_data]);
 | 
				
			||||||
@ -141,6 +141,9 @@ if ((($testid = trim($_REQUEST["testid"] ?: "")) !== "") &&
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch ($action) {
 | 
				
			||||||
        case "save_answer":
 | 
					        case "save_answer":
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                $chidx = $_REQUEST["challenge_index"];
 | 
					                $chidx = $_REQUEST["challenge_index"];
 | 
				
			||||||
@ -313,12 +316,21 @@ switch ($action) {
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            $gameid = trim($_REQUEST["gameid"] ?: "");
 | 
					            $gameid = trim($_REQUEST["gameid"] ?: "");
 | 
				
			||||||
            $filter = trim($_REQUEST["filter"] ?: "");
 | 
					            $filter = trim($_REQUEST["filter"] ?: "");
 | 
				
			||||||
 | 
					            $ordering = trim($_REQUEST["orderby"] ?: "");
 | 
				
			||||||
            if (($gameid !== "") && (is_user_contributor_to_game($gameid, $nickname) || $is_quizmaster)) {
 | 
					            if (($gameid !== "") && (is_user_contributor_to_game($gameid, $nickname) || $is_quizmaster)) {
 | 
				
			||||||
                $game_results = get_results_by_gameid($gameid, $filter);
 | 
					                $game_results = get_results_by_gameid($gameid, $filter, $ordering, true);
 | 
				
			||||||
                $result = json_encode($game_results);
 | 
					                $result = json_encode($game_results);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
 | 
					    case "generate_detailed_stats":
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            $testids = json_decode(trim($_REQUEST["testids"] ?: "[]"), true);
 | 
				
			||||||
 | 
					            $gameid = trim($_REQUEST["gameid"] ?: "");
 | 
				
			||||||
 | 
					            $stats = generate_detailed_stats($gameid, $testids);
 | 
				
			||||||
 | 
					            $result = json_encode($stats);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// quizmaster actions
 | 
					// quizmaster actions
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										15
									
								
								js/common.js
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								js/common.js
									
									
									
									
									
								
							@ -31,3 +31,18 @@ function time_to_seconds(t) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    return s;
 | 
					    return s;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -339,6 +339,10 @@ function list_results_by_game(game) {
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                test_summary_record.addEventListener("click", () => {
 | 
				
			||||||
 | 
					                    window.open(`testground.php?testid=${record["_id"]}&view_only=true`, "_blank");
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                test_group_box.appendChild(test_summary_record);
 | 
					                test_group_box.appendChild(test_summary_record);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                n--;
 | 
					                n--;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
function create_cell(content) {
 | 
					function create_cell(content = "") {
 | 
				
			||||||
    let cell = document.createElement("td");
 | 
					    let cell = document.createElement("td");
 | 
				
			||||||
    cell.innerHTML = content;
 | 
					    cell.innerHTML = content;
 | 
				
			||||||
    return cell;
 | 
					    return cell;
 | 
				
			||||||
@ -7,8 +7,14 @@ function create_cell(content) {
 | 
				
			|||||||
function fetch_results() {
 | 
					function fetch_results() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let filterF = document.getElementById("filter");
 | 
					    let filterF = document.getElementById("filter");
 | 
				
			||||||
 | 
					    let orderbyF = document.getElementById("orderby");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let req = {action: "get_results_by_gameid", gameid: GAMEID, filter: filterF.value.trim()};
 | 
					    let req = {
 | 
				
			||||||
 | 
					        action: "get_results_by_gameid",
 | 
				
			||||||
 | 
					        gameid: GAMEID,
 | 
				
			||||||
 | 
					        filter: filterF.value.trim(),
 | 
				
			||||||
 | 
					        orderby: orderbyF.value.trim()
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    request(req).then(resp => {
 | 
					    request(req).then(resp => {
 | 
				
			||||||
        let rd = document.getElementById("results_display");
 | 
					        let rd = document.getElementById("results_display");
 | 
				
			||||||
@ -28,8 +34,9 @@ function fetch_results() {
 | 
				
			|||||||
            // is the game concluded
 | 
					            // is the game concluded
 | 
				
			||||||
            let concluded = record["state"] === "concluded";
 | 
					            let concluded = record["state"] === "concluded";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            let percentage = "-";
 | 
					            let percentage = "";
 | 
				
			||||||
            let timestamp = "-";
 | 
					            let start_timestamp = unix_time_to_human_readable(record["start_time"]);
 | 
				
			||||||
 | 
					            let end_timestamp = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // replace some fields if game was concluded
 | 
					            // replace some fields if game was concluded
 | 
				
			||||||
            if (concluded) {
 | 
					            if (concluded) {
 | 
				
			||||||
@ -39,16 +46,31 @@ function fetch_results() {
 | 
				
			|||||||
                percentage = `${r}%`;
 | 
					                percentage = `${r}%`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                // finish timestamp
 | 
					                // finish timestamp
 | 
				
			||||||
                timestamp = unix_time_to_human_readable(record["end_time"]);
 | 
					                end_timestamp = unix_time_to_human_readable(record["end_time"]);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // create cells
 | 
					            // create cells
 | 
				
			||||||
            let empty_cell = create_cell("");
 | 
					            let selectChk = document.createElement("input");
 | 
				
			||||||
 | 
					            selectChk.type = "checkbox";
 | 
				
			||||||
 | 
					            selectChk.name = "game_select";
 | 
				
			||||||
 | 
					            selectChk.record = record;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let selection_cell = create_cell();
 | 
				
			||||||
 | 
					            selection_cell.append(selectChk);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let id_cell = create_cell(record["_id"]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            let inspect_link = `<a href="testground.php?testid=${record["_id"]}&view_only=true" target="_blank">🔎</a>`
 | 
				
			||||||
 | 
					            let inspect_cell = create_cell(inspect_link);
 | 
				
			||||||
            let name_cell = create_cell(record.nickname)
 | 
					            let name_cell = create_cell(record.nickname)
 | 
				
			||||||
            let percentage_cell = create_cell(percentage)
 | 
					            let percentage_cell = create_cell(percentage)
 | 
				
			||||||
            let timestamp_cell = create_cell(timestamp);
 | 
					            let start_timestamp_cell = create_cell(start_timestamp);
 | 
				
			||||||
 | 
					            let end_timestamp_cell = create_cell(end_timestamp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            row.append(empty_cell, name_cell, percentage_cell, timestamp_cell);
 | 
					            row.append(selection_cell, id_cell, inspect_cell, name_cell, percentage_cell, start_timestamp_cell, end_timestamp_cell);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // save record data into the row object
 | 
				
			||||||
 | 
					            row.record = record;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // append row
 | 
					            // append row
 | 
				
			||||||
            rd.appendChild(row);
 | 
					            rd.appendChild(row);
 | 
				
			||||||
@ -56,3 +78,91 @@ function fetch_results() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function generate_report() {
 | 
				
			||||||
 | 
					    let testids = [];
 | 
				
			||||||
 | 
					    let game_selectChks = document.getElementsByName("game_select");
 | 
				
			||||||
 | 
					    game_selectChks.forEach((chk) => {
 | 
				
			||||||
 | 
					        if (chk.checked) {
 | 
				
			||||||
 | 
					            testids.push(chk.record["_id"]);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let req = {
 | 
				
			||||||
 | 
					        action: "generate_detailed_stats",
 | 
				
			||||||
 | 
					        gameid: GAMEID,
 | 
				
			||||||
 | 
					        testids: JSON.stringify(testids)
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    request(req).then((resp) => {
 | 
				
			||||||
 | 
					        let stats = JSON.parse(resp);
 | 
				
			||||||
 | 
					        let statsTab = window.open("report.html", "_blank");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        statsTab.addEventListener("load", () => {
 | 
				
			||||||
 | 
					            let report_display = statsTab.document.getElementById("report_display");
 | 
				
			||||||
 | 
					            report_display.innerHTML = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            stats.forEach((challenge) => {
 | 
				
			||||||
 | 
					                let challenge_box = document.createElement("section");
 | 
				
			||||||
 | 
					                challenge_box.classList.add("challenge");
 | 
				
			||||||
 | 
					                challenge_box.style.width = "100%";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                let img_url = challenge["image_url"];
 | 
				
			||||||
 | 
					                if (img_url !== "") {
 | 
				
			||||||
 | 
					                    let fig = document.createElement("img");
 | 
				
			||||||
 | 
					                    fig.src = img_url;
 | 
				
			||||||
 | 
					                    fig.classList.add("question-image");
 | 
				
			||||||
 | 
					                    challenge_box.append(fig);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                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 n = challenge["answer_count"];
 | 
				
			||||||
 | 
					                for (let i = 0; i < n; i++) {
 | 
				
			||||||
 | 
					                    let answer = challenge["answers"][i];
 | 
				
			||||||
 | 
					                    let correct_answer = answer === challenge["correct_answer"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    let answer_section = document.createElement("section");
 | 
				
			||||||
 | 
					                    answer_section.classList.add("answer");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    let progress_bar_container = document.createElement("section");
 | 
				
			||||||
 | 
					                    progress_bar_container.classList.add("pb-container")
 | 
				
			||||||
 | 
					                    let progress_bar_indicator = document.createElement("section");
 | 
				
			||||||
 | 
					                    progress_bar_indicator.classList.add("pb-indicator")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    let percentage = challenge["answer_ratio"][i] * 100;
 | 
				
			||||||
 | 
					                    progress_bar_indicator.style.width = `${percentage}%`;
 | 
				
			||||||
 | 
					                    progress_bar_indicator.innerText = Math.round(percentage * 100.0) / 100.0 + "%";
 | 
				
			||||||
 | 
					                    progress_bar_indicator.setAttribute("correct", correct_answer ? "true" : "false");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    progress_bar_container.append(progress_bar_indicator);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    let answer_text = document.createElement("span");
 | 
				
			||||||
 | 
					                    answer_text.classList.add("answer");
 | 
				
			||||||
 | 
					                    answer_text.innerHTML = preprocess_inserts(answer);
 | 
				
			||||||
 | 
					                    answer_text.setAttribute("correct", correct_answer ? "true" : "false");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    answer_section.append(progress_bar_container, answer_text);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    answer_container.append(answer_section);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                report_display.append(challenge_box);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            statsTab.MathJax.typeset();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function toggle_test_selection() {
 | 
				
			||||||
 | 
					    let game_selectChks = document.getElementsByName("game_select");
 | 
				
			||||||
 | 
					    game_selectChks.forEach((chk) => {
 | 
				
			||||||
 | 
					        chk.checked = !chk.checked;
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -1,7 +1,7 @@
 | 
				
			|||||||
let TEST_DATA = {}
 | 
					let TEST_DATA = {}
 | 
				
			||||||
let INTERVAL_HANDLE = null;
 | 
					let INTERVAL_HANDLE = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function populate_infobox(test_data) {
 | 
					function populate_infobox(test_data, view_only) {
 | 
				
			||||||
    if (INTERVAL_HANDLE !== null) {
 | 
					    if (INTERVAL_HANDLE !== null) {
 | 
				
			||||||
        clearInterval(INTERVAL_HANDLE);
 | 
					        clearInterval(INTERVAL_HANDLE);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -12,6 +12,9 @@ function populate_infobox(test_data) {
 | 
				
			|||||||
    let durationS = document.getElementById("duration");
 | 
					    let durationS = document.getElementById("duration");
 | 
				
			||||||
    let percentageS = document.getElementById("percentage");
 | 
					    let percentageS = document.getElementById("percentage");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let submitBtn = document.getElementById("submit_btn");
 | 
				
			||||||
 | 
					    submitBtn.hidden = view_only;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    game_nameS.innerHTML = test_data["gamename"];
 | 
					    game_nameS.innerHTML = test_data["gamename"];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (test_concluded) {
 | 
					    if (test_concluded) {
 | 
				
			||||||
@ -28,19 +31,27 @@ function populate_infobox(test_data) {
 | 
				
			|||||||
        hide("ongoing-info");
 | 
					        hide("ongoing-info");
 | 
				
			||||||
        show("concluded-info");
 | 
					        show("concluded-info");
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
 | 
					        let timerS = document.getElementById("timer");
 | 
				
			||||||
 | 
					        let time_left_s = Number(test_data["end_limit_time"]) - Number(test_data["current_time"]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let print_timer = () => {
 | 
				
			||||||
 | 
					            timerS.innerHTML = seconds_to_time(time_left_s);
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (test_data["time_limited"]) {
 | 
					        if (test_data["time_limited"]) {
 | 
				
			||||||
            let timerS = document.getElementById("timer");
 | 
					            print_timer()
 | 
				
			||||||
            let time_left_s = Number(test_data["end_limit_time"]) - Number(test_data["current_time"]);
 | 
					
 | 
				
			||||||
            seconds_to_time(time_left_s);
 | 
					            if (!view_only) {
 | 
				
			||||||
            INTERVAL_HANDLE = setInterval(() => {
 | 
					                INTERVAL_HANDLE = setInterval(() => {
 | 
				
			||||||
                time_left_s--;
 | 
					                    time_left_s--;
 | 
				
			||||||
                timerS.innerHTML = seconds_to_time(time_left_s);
 | 
					                    print_timer();
 | 
				
			||||||
                if (time_left_s <= 0) {
 | 
					                    if (time_left_s <= 0) {
 | 
				
			||||||
                    populate_all(test_data["_id"]);
 | 
					                        populate_all(test_data["_id"]);
 | 
				
			||||||
                    clearInterval(INTERVAL_HANDLE);
 | 
					                        clearInterval(INTERVAL_HANDLE);
 | 
				
			||||||
                    INTERVAL_HANDLE = null;
 | 
					                        INTERVAL_HANDLE = null;
 | 
				
			||||||
                }
 | 
					                    }
 | 
				
			||||||
            }, 1000);
 | 
					                }, 1000);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
            show("time-info");
 | 
					            show("time-info");
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            hide("time-info");
 | 
					            hide("time-info");
 | 
				
			||||||
@ -59,7 +70,7 @@ function populate_infobox(test_data) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function populate_challenges(test_data) {
 | 
					function populate_challenges(test_data, view_only = false) {
 | 
				
			||||||
    let test_display = document.getElementById("test_display");
 | 
					    let test_display = document.getElementById("test_display");
 | 
				
			||||||
    test_display.innerHTML = "";
 | 
					    test_display.innerHTML = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -99,7 +110,7 @@ function populate_challenges(test_data) {
 | 
				
			|||||||
            answer_radio.type = "radio";
 | 
					            answer_radio.type = "radio";
 | 
				
			||||||
            answer_radio.id = `${challenge_N}_${answer_N}`;
 | 
					            answer_radio.id = `${challenge_N}_${answer_N}`;
 | 
				
			||||||
            answer_radio.name = `challenge_${challenge_N}`;
 | 
					            answer_radio.name = `challenge_${challenge_N}`;
 | 
				
			||||||
            answer_radio.disabled = test_concluded;
 | 
					            answer_radio.disabled = test_concluded || view_only;
 | 
				
			||||||
            let answer_N_snapshot = answer_N;
 | 
					            let answer_N_snapshot = answer_N;
 | 
				
			||||||
            answer_radio.addEventListener("input", () => {
 | 
					            answer_radio.addEventListener("input", () => {
 | 
				
			||||||
                save_answer(challenge_N_snapshot, answer_N_snapshot);
 | 
					                save_answer(challenge_N_snapshot, answer_N_snapshot);
 | 
				
			||||||
@ -132,15 +143,16 @@ function populate_challenges(test_data) {
 | 
				
			|||||||
    MathJax.typeset();
 | 
					    MathJax.typeset();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function populate_all(test_id) {
 | 
					function populate_all(test_id, view_only) {
 | 
				
			||||||
    let req = {
 | 
					    let req = {
 | 
				
			||||||
        action: "get_test",
 | 
					        action: "get_test",
 | 
				
			||||||
 | 
					        view_only: view_only,
 | 
				
			||||||
        testid: test_id
 | 
					        testid: test_id
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    request(req).then(resp => {
 | 
					    request(req).then(resp => {
 | 
				
			||||||
        TEST_DATA = JSON.parse(resp);
 | 
					        TEST_DATA = JSON.parse(resp);
 | 
				
			||||||
        populate_challenges(TEST_DATA);
 | 
					        populate_challenges(TEST_DATA, view_only);
 | 
				
			||||||
        populate_infobox(TEST_DATA);
 | 
					        populate_infobox(TEST_DATA, view_only);
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -154,21 +166,6 @@ function save_answer(chidx, aidx) {
 | 
				
			|||||||
    request(req);
 | 
					    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() {
 | 
					function submit_test() {
 | 
				
			||||||
    let req = {
 | 
					    let req = {
 | 
				
			||||||
        action: "submit_test",
 | 
					        action: "submit_test",
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										20
									
								
								report.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								report.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					<!DOCTYPE html>
 | 
				
			||||||
 | 
					<html lang="en">
 | 
				
			||||||
 | 
					<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 - Részletes jelentés</title>
 | 
				
			||||||
 | 
					    <script src="js/req.js"></script>
 | 
				
			||||||
 | 
					    <script src="js/spreadquiz.js"></script>
 | 
				
			||||||
 | 
					    <script src="js/o.js"></script>
 | 
				
			||||||
 | 
					    <script src="js/common.js"></script>
 | 
				
			||||||
 | 
					    <link rel="stylesheet" href="style/spreadquiz.css">
 | 
				
			||||||
 | 
					    <link rel="stylesheet" href="style/quizmaster_area.css"/>
 | 
				
			||||||
 | 
					    <script id="MathJax-script" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
 | 
				
			||||||
 | 
					</head>
 | 
				
			||||||
 | 
					<body>
 | 
				
			||||||
 | 
					<section id="report_display">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</section>
 | 
				
			||||||
 | 
					</body>
 | 
				
			||||||
 | 
					</html>
 | 
				
			||||||
@ -41,13 +41,23 @@ if (!is_user_contributor_to_game($game_id, $user_data["nickname"]) && ($user_dat
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<section style="margin-bottom: 0.3em">
 | 
					<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="text" placeholder="Szűrőfeltétel" id="filter" style="font-family: 'Monaco', monospace; width: 50em;">
 | 
				
			||||||
 | 
					    <input type="text" placeholder="Rendezés" id="orderby" style="font-family: 'Monaco', monospace; width: 50em;">
 | 
				
			||||||
    <input type="button" value="Szűrés" onclick="fetch_results()">
 | 
					    <input type="button" value="Szűrés" onclick="fetch_results()">
 | 
				
			||||||
 | 
					    <input type="button" value="Jelentés előállítása" onclick="generate_report()">
 | 
				
			||||||
</section>
 | 
					</section>
 | 
				
			||||||
<section>
 | 
					<section>
 | 
				
			||||||
    <section id="table_section">
 | 
					    <section id="table_section">
 | 
				
			||||||
        <table class="management">
 | 
					        <table class="management">
 | 
				
			||||||
            <thead>
 | 
					            <thead>
 | 
				
			||||||
            <tr>
 | 
					            <tr>
 | 
				
			||||||
 | 
					                <th>
 | 
				
			||||||
 | 
					                    <input type="checkbox" onclick="toggle_test_selection()">
 | 
				
			||||||
 | 
					                </th>
 | 
				
			||||||
 | 
					                <th>#<br>
 | 
				
			||||||
 | 
					                    <code>
 | 
				
			||||||
 | 
					                        [_id]
 | 
				
			||||||
 | 
					                    </code>
 | 
				
			||||||
 | 
					                </th>
 | 
				
			||||||
                <th></th>
 | 
					                <th></th>
 | 
				
			||||||
                <th>
 | 
					                <th>
 | 
				
			||||||
                    Felhasználónév<br>
 | 
					                    Felhasználónév<br>
 | 
				
			||||||
@ -60,11 +70,17 @@ if (!is_user_contributor_to_game($game_id, $user_data["nickname"]) && ($user_dat
 | 
				
			|||||||
                        [result]
 | 
					                        [result]
 | 
				
			||||||
                    </code>
 | 
					                    </code>
 | 
				
			||||||
                </th>
 | 
					                </th>
 | 
				
			||||||
                <th>Időbélyeg<br>
 | 
					                <th>Kezdés ideje<br>
 | 
				
			||||||
                    <code>
 | 
					                    <code>
 | 
				
			||||||
                        [timestamp]
 | 
					                        [start_time]
 | 
				
			||||||
                    </code>
 | 
					                    </code>
 | 
				
			||||||
                </th>
 | 
					                </th>
 | 
				
			||||||
 | 
					                <th>Befejezés ideje<br>
 | 
				
			||||||
 | 
					                    <code>
 | 
				
			||||||
 | 
					                        [end_time]
 | 
				
			||||||
 | 
					                    </code>
 | 
				
			||||||
 | 
					                </th>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            </tr>
 | 
					            </tr>
 | 
				
			||||||
            </thead>
 | 
					            </thead>
 | 
				
			||||||
            <tbody id="results_display">
 | 
					            <tbody id="results_display">
 | 
				
			||||||
@ -74,6 +90,7 @@ if (!is_user_contributor_to_game($game_id, $user_data["nickname"]) && ($user_dat
 | 
				
			|||||||
</section>
 | 
					</section>
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
    let GAMEID = <?="$game_id"?>;
 | 
					    let GAMEID = <?="$game_id"?>;
 | 
				
			||||||
 | 
					    fetch_results();
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
</body>
 | 
					</body>
 | 
				
			||||||
</html>
 | 
					</html>
 | 
				
			||||||
 | 
				
			|||||||
@ -94,3 +94,38 @@ section.window-inner tr td:first-of-type {
 | 
				
			|||||||
    padding: 0.2em;
 | 
					    padding: 0.2em;
 | 
				
			||||||
    color: #671b17;
 | 
					    color: #671b17;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					a {
 | 
				
			||||||
 | 
					    text-decoration: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					section.pb-container {
 | 
				
			||||||
 | 
					    display: inline-block;
 | 
				
			||||||
 | 
					    width: 8em;
 | 
				
			||||||
 | 
					    height: 1em;
 | 
				
			||||||
 | 
					    margin-right: 1em;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					section.pb-indicator {
 | 
				
			||||||
 | 
					    height: 100%;
 | 
				
			||||||
 | 
					    background-color: #a1d7d7;
 | 
				
			||||||
 | 
					    border-radius: 0.3em;
 | 
				
			||||||
 | 
					    font-size: 0.7em;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					section.pb-indicator[correct=true] {
 | 
				
			||||||
 | 
					    background-color: #176767;
 | 
				
			||||||
 | 
					    color: whitesmoke;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					section#report_display {
 | 
				
			||||||
 | 
					    width: fit-content;
 | 
				
			||||||
 | 
					    display: block;
 | 
				
			||||||
 | 
					    margin: auto;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					span.answer[correct=true] {
 | 
				
			||||||
 | 
					    color: whitesmoke;
 | 
				
			||||||
 | 
					    background-color: #176767;
 | 
				
			||||||
 | 
					    border-radius: 0.3em;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -9,6 +9,7 @@ if (!get_autologin_state() || !isset($_REQUEST["testid"])) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$testid = trim($_REQUEST["testid"] ?: "");
 | 
					$testid = trim($_REQUEST["testid"] ?: "");
 | 
				
			||||||
 | 
					$view_only = trim($_REQUEST["view_only"] ?: "false") === "true" ? "true" : "false";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if ($testid === "") {
 | 
					if ($testid === "") {
 | 
				
			||||||
    exit();
 | 
					    exit();
 | 
				
			||||||
@ -39,25 +40,31 @@ if ($testid === "") {
 | 
				
			|||||||
<section id="infobox">
 | 
					<section id="infobox">
 | 
				
			||||||
    <span id="game_name"></span>
 | 
					    <span id="game_name"></span>
 | 
				
			||||||
    <section>
 | 
					    <section>
 | 
				
			||||||
 | 
					        <?php if ($view_only) { ?>
 | 
				
			||||||
 | 
					            <section style="margin-bottom: 1em">
 | 
				
			||||||
 | 
					                CSAK OLVASHATÓ,<br>
 | 
				
			||||||
 | 
					                NEM FRISSÜL AUTOMATIKUSAN!
 | 
				
			||||||
 | 
					            </section>
 | 
				
			||||||
 | 
					        <?php } ?>
 | 
				
			||||||
        <section id="ongoing-info">
 | 
					        <section id="ongoing-info">
 | 
				
			||||||
            <section id="time-info" shown="false">
 | 
					            <section id="time-info" shown="false">
 | 
				
			||||||
                <span class="infobox-description">Hátralevő idő:</span>
 | 
					                <span class="infobox-description">Hátralevő idő:</span>
 | 
				
			||||||
                <section id="timer">10:00:00</section>
 | 
					                <section id="timer">00:00:00</section>
 | 
				
			||||||
            </section>
 | 
					            </section>
 | 
				
			||||||
            <section id="further-info">
 | 
					            <section id="further-info">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            </section>
 | 
					            </section>
 | 
				
			||||||
            <input type="button" value="Beküld" onclick="submit_test()">
 | 
					            <input type="button" value="Beküld" onclick="submit_test()" id="submit_btn">
 | 
				
			||||||
        </section>
 | 
					        </section>
 | 
				
			||||||
        <section id="concluded-info">
 | 
					        <section id="concluded-info">
 | 
				
			||||||
            <section id="duration"></section>
 | 
					            <section id="duration"></section>
 | 
				
			||||||
            <span class="infobox-description">Eredmény:</span>
 | 
					            <span class="infobox-description">Eredmény:</span>
 | 
				
			||||||
            <section id="percentage">95% (19/20)</section>
 | 
					            <section id="percentage">100% (20/20)</section>
 | 
				
			||||||
        </section>
 | 
					        </section>
 | 
				
			||||||
    </section>
 | 
					    </section>
 | 
				
			||||||
</section>
 | 
					</section>
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
    populate_all("<?=$testid ?>");
 | 
					    populate_all("<?=$testid ?>", <?=$view_only ?>);
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
</body>
 | 
					</body>
 | 
				
			||||||
</html>
 | 
					</html>
 | 
				
			||||||
							
								
								
									
										151
									
								
								testmgr.php
									
									
									
									
									
								
							
							
						
						
									
										151
									
								
								testmgr.php
									
									
									
									
									
								
							@ -183,7 +183,23 @@ function conclude_test(string $testid)
 | 
				
			|||||||
    update_test($test_data);
 | 
					    update_test($test_data);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function split_criterion(string $crstr): array {
 | 
					function automatic_typecast(string $rval)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if (is_numeric($rval)) { // is it a numeric value?
 | 
				
			||||||
 | 
					        if (((int)$rval) == ((double)$rval)) { // is it an integer?
 | 
				
			||||||
 | 
					            return (int)$rval;
 | 
				
			||||||
 | 
					        } else { // is it a float?
 | 
				
			||||||
 | 
					            return (double)$rval;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } elseif (str_starts_with($rval, 'T')) { // is it a date/time value?
 | 
				
			||||||
 | 
					        return strtotime(substr($rval, 1)); // convert to UNIX timestamp
 | 
				
			||||||
 | 
					    } else { // it's a string
 | 
				
			||||||
 | 
					        return substr($rval, 1, strlen($rval) - 2); // strip leading and trailing quotes
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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);
 | 
					    preg_match("/([<>=!]+|LIKE|NOT LIKE|IN|NOT IN|CONTAINS|NOT CONTAINS|BETWEEN|NOT BETWEEN|EXISTS)/", $crstr, $matches, PREG_OFFSET_CAPTURE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // extract operator
 | 
					    // extract operator
 | 
				
			||||||
@ -195,10 +211,18 @@ function split_criterion(string $crstr): array {
 | 
				
			|||||||
    $right = trim(substr($crstr, $op_pos + strlen($op), strlen($crstr)));
 | 
					    $right = trim(substr($crstr, $op_pos + strlen($op), strlen($crstr)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // automatic type conversion
 | 
					    // automatic type conversion
 | 
				
			||||||
    if (($right[0] !== "\"") && ($right[0] !== "\'")) { // numeric value
 | 
					    if (str_starts_with($right, "[") && str_ends_with($right, "]")) { // is it an array?
 | 
				
			||||||
        $right = (int) $right;
 | 
					        $right = substr($right, 1, -1); // strip leading and trailing brackets
 | 
				
			||||||
    } else { // string value
 | 
					        $elements = explode(",", $right); // extract array elements
 | 
				
			||||||
        $right = substr($right, 1, strlen($right) - 2); // strip leading and trailing quotes
 | 
					        $right = []; // re-init right value, since it's an array
 | 
				
			||||||
 | 
					        foreach ($elements as $element) { // insert array elements
 | 
				
			||||||
 | 
					            $element = trim($element);
 | 
				
			||||||
 | 
					            if ($element !== "") {
 | 
				
			||||||
 | 
					                $right[] = automatic_typecast($element);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    } else { // it must be a single value
 | 
				
			||||||
 | 
					        $right = automatic_typecast($right);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return [$left, $op, $right];
 | 
					    return [$left, $op, $right];
 | 
				
			||||||
@ -228,7 +252,10 @@ function build_query(string $filter): array
 | 
				
			|||||||
            $k++;
 | 
					            $k++;
 | 
				
			||||||
        } elseif ($c === ")") {
 | 
					        } elseif ($c === ")") {
 | 
				
			||||||
            $k--;
 | 
					            $k--;
 | 
				
			||||||
        } else {
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // only omit parentheses at the top-level expression
 | 
				
			||||||
 | 
					        if (!((($c === "(") && ($k === 1)) || (($c === ")") && ($k === 0)))) {
 | 
				
			||||||
            $buffer .= $c;
 | 
					            $buffer .= $c;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -266,21 +293,129 @@ function build_query(string $filter): array
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    return $criteria;
 | 
					    return $criteria;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function get_results_by_gameid(string $gameid, string $filter): array
 | 
					function build_ordering(string $orderby): array
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    // don't process empty order instructions
 | 
				
			||||||
 | 
					    if ($orderby === "") {
 | 
				
			||||||
 | 
					        return [];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // explode string at tokens delimiting separate order criteria
 | 
				
			||||||
 | 
					    $ordering = [];
 | 
				
			||||||
 | 
					    $subcriteria = explode(";", $orderby);
 | 
				
			||||||
 | 
					    foreach ($subcriteria as $subcriterion) {
 | 
				
			||||||
 | 
					        $parts = explode(":", $subcriterion); // fetch parts
 | 
				
			||||||
 | 
					        $field_name = trim($parts[0], "\ \n\r\t\v\0\"'"); // strip leading and trailing quotes if exists
 | 
				
			||||||
 | 
					        $direction = strtolower(trim($parts[1])); // fetch ordering direction
 | 
				
			||||||
 | 
					        $ordering[$field_name] = $direction; // build ordering instruction
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return $ordering;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function get_results_by_gameid(string $gameid, string $filter, string $orderby, bool $exclude_challenge_data): array
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    global $testdb;
 | 
					    global $testdb;
 | 
				
			||||||
    $qb = $testdb->createQueryBuilder();
 | 
					    $qb = $testdb->createQueryBuilder();
 | 
				
			||||||
    $qb = $qb->where(["gameid", "=", (int)$gameid]);
 | 
					    $qb = $qb->where(["gameid", "=", (int)$gameid]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // filtering
 | 
				
			||||||
    if (trim($filter) !== "") {
 | 
					    if (trim($filter) !== "") {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // auto complete starting and ending parenthesis
 | 
				
			||||||
 | 
					        if (!str_starts_with($filter, "(")) {
 | 
				
			||||||
 | 
					            $filter = "(" . $filter;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (!str_ends_with($filter, ")")) {
 | 
				
			||||||
 | 
					            $filter = $filter . ")";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $criteria = build_query($filter);
 | 
					        $criteria = build_query($filter);
 | 
				
			||||||
        $qb->where($criteria);
 | 
					        $qb->where($criteria);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ordering
 | 
				
			||||||
 | 
					    if (trim($orderby) !== "") {
 | 
				
			||||||
 | 
					        $ordering = build_ordering($orderby);
 | 
				
			||||||
 | 
					        $qb->orderBy($ordering);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // excluding challenge data
 | 
				
			||||||
 | 
					    if ($exclude_challenge_data) {
 | 
				
			||||||
 | 
					        $qb->except(["challenges"]);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    $test_data_array = $qb->getQuery()->fetch();
 | 
					    $test_data_array = $qb->getQuery()->fetch();
 | 
				
			||||||
    return $test_data_array;
 | 
					    return $test_data_array;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function generate_detailed_stats(string $gameid, array $testids): array
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    if ((count($testids) === 0) || ($gameid === "")) {
 | 
				
			||||||
 | 
					        return [];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    global $testdb;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // fetch relevant entries
 | 
				
			||||||
 | 
					    $qb = $testdb->createQueryBuilder();
 | 
				
			||||||
 | 
					    $criteria = [["gameid", "=", (int)$gameid], "AND", ["state", "=", "concluded"], "AND", ["_id", "IN", $testids]];
 | 
				
			||||||
 | 
					    $qb->where($criteria);
 | 
				
			||||||
 | 
					    $qb->select(["challenges"]);
 | 
				
			||||||
 | 
					    $entries = $qb->getQuery()->fetch();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    $challenge_indices = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // count answers
 | 
				
			||||||
 | 
					    $aggregated = [];
 | 
				
			||||||
 | 
					    foreach ($entries as $entry) {
 | 
				
			||||||
 | 
					        foreach ($entry["challenges"] as $challenge) {
 | 
				
			||||||
 | 
					            $correct_answer = $challenge["answers"][$challenge["correct_answer"]];
 | 
				
			||||||
 | 
					            $compound = $challenge["question"] . $correct_answer . count($challenge["answers"]) . $challenge["image_url"];
 | 
				
			||||||
 | 
					            $idhash = md5($compound);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // if this is a new challenge to the list...
 | 
				
			||||||
 | 
					            if (!isset($challenge_indices[$idhash])) {
 | 
				
			||||||
 | 
					                $challenge_indices[$idhash] = count($challenge_indices);
 | 
				
			||||||
 | 
					                $challenge_info = [ // copy challenge info
 | 
				
			||||||
 | 
					                    "hash" => $idhash,
 | 
				
			||||||
 | 
					                    "image_url" => $challenge["image_url"],
 | 
				
			||||||
 | 
					                    "question" => $challenge["question"],
 | 
				
			||||||
 | 
					                    "answers" => $challenge["answers"],
 | 
				
			||||||
 | 
					                    "correct_answer" => $correct_answer,
 | 
				
			||||||
 | 
					                    "player_answers" => array_fill(0, count($challenge["answers"]), 0),
 | 
				
			||||||
 | 
					                    "answer_count" => count($challenge["answers"]),
 | 
				
			||||||
 | 
					                ];
 | 
				
			||||||
 | 
					                $aggregated[$challenge_indices[$idhash]] = $challenge_info; // insert challenge info
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // fetch challenge index
 | 
				
			||||||
 | 
					            $challenge_idx = $challenge_indices[$idhash];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // add up player answer
 | 
				
			||||||
 | 
					            $answer_idx = array_search($challenge["answers"][$challenge["player_answer"]], $aggregated[$challenge_idx]["answers"]); // transform player answer index to report answer index
 | 
				
			||||||
 | 
					            $aggregated[$challenge_idx]["player_answers"][(int)$answer_idx]++;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // produce derived info
 | 
				
			||||||
 | 
					    foreach ($aggregated as &$entry) {
 | 
				
			||||||
 | 
					        $entry["answer_ratio"] = $entry["player_answers"];
 | 
				
			||||||
 | 
					        $answer_n = count($entry["answer_ratio"]);
 | 
				
			||||||
 | 
					        $sum = array_sum($entry["player_answers"]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($sum === 0) {
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for ($i = 0; $i < $answer_n; $i++) {
 | 
				
			||||||
 | 
					            $entry["answer_ratio"][$i] = $entry["player_answers"][$i] / $sum;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // match challenges
 | 
				
			||||||
 | 
					    return $aggregated;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user