- function doing report generation has been beautified
- report generation windows was added to game manager - full TeX source downloading compressed into a ZIP
This commit is contained in:
parent
a01b1f1760
commit
1fff851fb5
@ -18,7 +18,8 @@ class TestSummary
|
|||||||
private float $percentage; // Ratio of correct answers
|
private float $percentage; // Ratio of correct answers
|
||||||
|
|
||||||
// Calculate percentage.
|
// Calculate percentage.
|
||||||
private function calculatePercentage() : void {
|
private function calculatePercentage(): void
|
||||||
|
{
|
||||||
if ($this->challengeN > 0) {
|
if ($this->challengeN > 0) {
|
||||||
$this->percentage = $this->correctAnswerN / (double)$this->challengeN * 100.0;
|
$this->percentage = $this->correctAnswerN / (double)$this->challengeN * 100.0;
|
||||||
} else { // avoid division by zero
|
} else { // avoid division by zero
|
||||||
@ -288,12 +289,12 @@ class Test extends AutoStoring
|
|||||||
return $this->gameId;
|
return $this->gameId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isConcluded() : bool
|
public function isConcluded(): bool
|
||||||
{
|
{
|
||||||
return $this->state === self::TEST_CONCLUDED;
|
return $this->state === self::TEST_CONCLUDED;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isOngoing() : bool
|
public function isOngoing(): bool
|
||||||
{
|
{
|
||||||
return $this->state === self::TEST_ONGOING;
|
return $this->state === self::TEST_ONGOING;
|
||||||
}
|
}
|
||||||
@ -332,25 +333,28 @@ class TestMgr
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update test in the database.
|
// Update test in the database.
|
||||||
function updateTest(Test &$test): void {
|
function updateTest(Test &$test): void
|
||||||
|
{
|
||||||
$a = $test->toArray();
|
$a = $test->toArray();
|
||||||
$this->db->update($a);
|
$this->db->update($a);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add test to the database.
|
// Add test to the database.
|
||||||
function addTest(Game &$game, User &$user): Test {
|
function addTest(Game &$game, User &$user): Test
|
||||||
|
{
|
||||||
// create new test
|
// create new test
|
||||||
$test = new Test($this,$game, $user);
|
$test = new Test($this, $game, $user);
|
||||||
|
|
||||||
// insert into database
|
// insert into database
|
||||||
$a = $test->toArray(["_id"]);
|
$a = $test->toArray(["_id"]);
|
||||||
$a = $this->db->insert($a);
|
$a = $this->db->insert($a);
|
||||||
|
|
||||||
$test = new Test($this,$a);
|
$test = new Test($this, $a);
|
||||||
return $test;
|
return $test;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addOrContinueTest(Game &$game, User &$user): Test|null {
|
function addOrContinueTest(Game &$game, User &$user): Test|null
|
||||||
|
{
|
||||||
// check if the user had taken this test before
|
// check if the user had taken this test before
|
||||||
$fetch_criteria = [["gameid", "=", (int)$game->getId()], "AND", ["nickname", "=", $user->getNickname()]];
|
$fetch_criteria = [["gameid", "=", (int)$game->getId()], "AND", ["nickname", "=", $user->getNickname()]];
|
||||||
$previous_tests = $this->db->findBy($fetch_criteria);
|
$previous_tests = $this->db->findBy($fetch_criteria);
|
||||||
@ -384,12 +388,14 @@ class TestMgr
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Delete test from the database.
|
// Delete test from the database.
|
||||||
function deleteTest(string $testid): void {
|
function deleteTest(string $testid): void
|
||||||
|
{
|
||||||
$this->db->deleteById($testid);
|
$this->db->deleteById($testid);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get concluded tests by game ID and nickname.
|
// Get concluded tests by game ID and nickname.
|
||||||
function getConcludedTests(string $gameid, string $nickname): array {
|
function getConcludedTests(string $gameid, string $nickname): array
|
||||||
|
{
|
||||||
$fetch_criteria = [["gameid", "=", (int)$gameid], "AND", ["nickname", "=", $nickname], "AND", ["state", "=", Test::TEST_CONCLUDED]];
|
$fetch_criteria = [["gameid", "=", (int)$gameid], "AND", ["nickname", "=", $nickname], "AND", ["state", "=", Test::TEST_CONCLUDED]];
|
||||||
$test_data_array = $this->db->findBy($fetch_criteria);
|
$test_data_array = $this->db->findBy($fetch_criteria);
|
||||||
$tests = [];
|
$tests = [];
|
||||||
@ -489,8 +495,10 @@ class TestMgr
|
|||||||
$challenge_idx = $challenge_indices[$idhash];
|
$challenge_idx = $challenge_indices[$idhash];
|
||||||
|
|
||||||
// add up player answer
|
// 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
|
if (trim($challenge["player_answer"]) !== "") {
|
||||||
$aggregated[$challenge_idx]["player_answers"][(int)$answer_idx]++;
|
$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]++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -514,7 +522,8 @@ class TestMgr
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Upgrade test. Just load tests and save them.
|
// Upgrade test. Just load tests and save them.
|
||||||
function upgradeTests(array $ids = []) : void {
|
function upgradeTests(array $ids = []): void
|
||||||
|
{
|
||||||
$a = [];
|
$a = [];
|
||||||
if ($ids === []) {
|
if ($ids === []) {
|
||||||
$a = $this->db->findAll();
|
$a = $this->db->findAll();
|
||||||
@ -529,10 +538,11 @@ class TestMgr
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Extract timed test IDs. Scan the database for tests with time limit ON.
|
// Extract timed test IDs. Scan the database for tests with time limit ON.
|
||||||
function extractExpiredTimedTestIds(bool $ongoingOnly = true) : array {
|
function extractExpiredTimedTestIds(bool $ongoingOnly = true): array
|
||||||
|
{
|
||||||
$query = [["time_limited", "=", true], "AND", ["end_limit_time", "<", time()]];
|
$query = [["time_limited", "=", true], "AND", ["end_limit_time", "<", time()]];
|
||||||
if ($ongoingOnly) {
|
if ($ongoingOnly) {
|
||||||
$query = [ ...$query, "AND", ["state", "=", TEST_ONGOING]];
|
$query = [...$query, "AND", ["state", "=", TEST_ONGOING]];
|
||||||
}
|
}
|
||||||
|
|
||||||
$qb = $this->db->createQueryBuilder();
|
$qb = $this->db->createQueryBuilder();
|
||||||
|
@ -116,7 +116,8 @@ if (!get_autologin_state() || (($user_data["privilege"] !== PRIVILEGE_CREATOR) &
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input type="button" value="Gyors áttekintés" 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}`)">
|
<input type="button" value="Részletes megjelenítés" onclick="window.open(`result_analyzer.php?game_id=${EDITED_GAME._id}`)"><br>
|
||||||
|
<input type="button" value="Csoportonkénti jelentés" onclick="show('report_generator')">
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
@ -138,6 +139,19 @@ if (!get_autologin_state() || (($user_data["privilege"] !== PRIVILEGE_CREATOR) &
|
|||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section class="window" shown="false" id="report_generator">
|
||||||
|
<section class="window-inner">
|
||||||
|
<input type="text" placeholder="Csoportok" value="" id="report_groups">
|
||||||
|
<input type="text" placeholder="Szűrő" value="" id="report_filter">
|
||||||
|
<select id="report_output_type">
|
||||||
|
<option value="pdf">PDF</option>
|
||||||
|
<option value="tex">TeX</option>
|
||||||
|
</select>
|
||||||
|
<input type="button" value="Előállítás" onclick="generate_game_report_by_groups()">
|
||||||
|
<input type="button" value="Bezárás" onclick="hide('report_generator')">
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
list_all_games();
|
list_all_games();
|
||||||
</script>
|
</script>
|
||||||
|
@ -16,8 +16,10 @@ const MAIN_URL = "main.php";
|
|||||||
const SESSION_NAME = "spreadquiz_sid";
|
const SESSION_NAME = "spreadquiz_sid";
|
||||||
const MAINTENANCE_FLAG_FILE = "MAINTENANCE";
|
const MAINTENANCE_FLAG_FILE = "MAINTENANCE";
|
||||||
const MISSING_IMAGE_PLACEHOLDER = "media/image-missing_120px.png";
|
const MISSING_IMAGE_PLACEHOLDER = "media/image-missing_120px.png";
|
||||||
const REPORT_DIR = DATADIR . DIRECTORY_SEPARATOR . "reports";
|
const REPORT_DIR = "report";
|
||||||
|
const REPORT_TEMPLATE_DIR = REPORT_DIR . DIRECTORY_SEPARATOR . "template";
|
||||||
|
const REPORT_PDF_OUTPUT_DIR = REPORT_DIR . DIRECTORY_SEPARATOR . "output";
|
||||||
|
const TEX_ENGINE = "xelatex";
|
||||||
|
|
||||||
session_name(SESSION_NAME);
|
session_name(SESSION_NAME);
|
||||||
|
|
||||||
@ -34,6 +36,7 @@ function init_datadir() {
|
|||||||
mkdir(DATADIR);
|
mkdir(DATADIR);
|
||||||
mkdir(GAMEMEDIA_DIR);
|
mkdir(GAMEMEDIA_DIR);
|
||||||
mkdir(REPORT_DIR);
|
mkdir(REPORT_DIR);
|
||||||
|
mkdir(REPORT_PDF_OUTPUT_DIR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -533,16 +533,35 @@ function generate_report_by_groups(ReqHandler &$rh, array $params): string
|
|||||||
|
|
||||||
$gameid = trim($params["gameid"]);
|
$gameid = trim($params["gameid"]);
|
||||||
$filter = trim($params["filter"] ?? "");
|
$filter = trim($params["filter"] ?? "");
|
||||||
$groups = explode_list(trim($params["groups"])); // TODO: lehessen több csoportra is
|
$groups = explode_list(trim($params["groups"]));
|
||||||
$outtype = trim($params["outtype"] ?? "pdf");
|
$outtype = trim($params["outtype"] ?? "pdf");
|
||||||
|
|
||||||
|
// only PDF and TEX are valid
|
||||||
|
if (!in_array($outtype, ["pdf", "tex"])) {
|
||||||
|
return "FAIL";
|
||||||
|
}
|
||||||
|
|
||||||
$game = $gameMgr->getGame($gameid);
|
$game = $gameMgr->getGame($gameid);
|
||||||
|
|
||||||
// verify game and access
|
// verify game and access
|
||||||
if (($game === null) || (!$game->isUserContributorOrOwner($user->getNickname()) || !$user->hasQuizmasterPrivilege())) {
|
if (($game === null) || ((!$game->isUserContributorOrOwner($user->getNickname()) && !$user->hasQuizmasterPrivilege()))) {
|
||||||
return "FAIL";
|
return "FAIL";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// create destination directory and copy TeX frame file
|
||||||
|
$repId = uniqid($outtype);
|
||||||
|
$buildDir = REPORT_PDF_OUTPUT_DIR . DIRECTORY_SEPARATOR . $repId;
|
||||||
|
mkdir($buildDir);
|
||||||
|
|
||||||
|
$iter = new RecursiveDirectoryIterator(REPORT_TEMPLATE_DIR, FilesystemIterator::SKIP_DOTS);
|
||||||
|
foreach ($iter as $dir_item) {
|
||||||
|
if (!$iter->isDir()) {
|
||||||
|
$src = $dir_item->getPathname();
|
||||||
|
$dst = $buildDir . DIRECTORY_SEPARATOR . $dir_item->getFilename();
|
||||||
|
copy($src, $dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// assemble report
|
// assemble report
|
||||||
$report = new Report($game->getName());
|
$report = new Report($game->getName());
|
||||||
foreach ($groups as $groupname) {
|
foreach ($groups as $groupname) {
|
||||||
@ -552,28 +571,39 @@ function generate_report_by_groups(ReqHandler &$rh, array $params): string
|
|||||||
}
|
}
|
||||||
|
|
||||||
// generate latex
|
// generate latex
|
||||||
$report_dir = "report";
|
$report->saveTeX($buildDir);
|
||||||
$content_dir = $report_dir . DIRECTORY_SEPARATOR . "stats";
|
|
||||||
$report->saveTeX($content_dir);
|
|
||||||
|
|
||||||
$output = "";
|
|
||||||
$contentType = "";
|
|
||||||
if ($outtype === "pdf") {
|
if ($outtype === "pdf") {
|
||||||
// run LuaLaTeX twice
|
// run LuaLaTeX twice
|
||||||
chdir($report_dir);
|
chdir($buildDir);
|
||||||
$tex_cmd = "lualatex -interaction=nonstopmode report.tex";
|
$tex_cmd = TEX_ENGINE . " -interaction=nonstopmode report.tex";
|
||||||
exec($tex_cmd);
|
exec($tex_cmd);
|
||||||
exec($tex_cmd);
|
exec($tex_cmd);
|
||||||
|
|
||||||
$output = "report.pdf";
|
// rename output
|
||||||
|
$origOutput = "report.pdf";
|
||||||
|
$output = $repId . ".pdf";
|
||||||
|
rename($origOutput, $output);
|
||||||
|
|
||||||
$contentType = "application/pdf";
|
$contentType = "application/pdf";
|
||||||
} else {
|
} else if ($outtype === "tex") {
|
||||||
$output = $content_dir . DIRECTORY_SEPARATOR . "content.tex";
|
$output = $buildDir . DIRECTORY_SEPARATOR . $repId . ".zip";
|
||||||
$contentType = "text/plain";
|
$zip = new ZipArchive();
|
||||||
|
$zip->open($output, ZipArchive::CREATE | ZipArchive::OVERWRITE);
|
||||||
|
$files = glob($buildDir . DIRECTORY_SEPARATOR . "*.tex");
|
||||||
|
foreach ($files as $file) {
|
||||||
|
$zip->addFile($file, basename($file));
|
||||||
|
}
|
||||||
|
$zip->addFromString("request.txt", "gameid: ${gameid}\ngroups: " . join(", ", $groups) . "\nfilter: ${filter}\nouttype: ${outtype}\n");
|
||||||
|
$zip->addFromString("request.url", $_SERVER["SERVER_NAME"] . ":" . $_SERVER["SERVER_PORT"] . $_SERVER["REQUEST_URI"]);
|
||||||
|
$zip->close();
|
||||||
|
$contentType = "application/zip";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set content type
|
||||||
header("Content-Type: ${contentType}");
|
header("Content-Type: ${contentType}");
|
||||||
header("Content-Length: " . filesize($output));
|
header("Content-Length: " . filesize($output));
|
||||||
|
header("Content-Disposition: attachment; filename=" . basename($output));
|
||||||
|
|
||||||
// deploy the generated PDF
|
// deploy the generated PDF
|
||||||
$f = fopen($output, "r");
|
$f = fopen($output, "r");
|
||||||
|
@ -371,6 +371,17 @@ function list_results_by_game(game) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function generate_game_report_by_groups() {
|
||||||
|
let gameid = EDITED_GAME["_id"];
|
||||||
|
let filter = document.getElementById("report_filter").value.trim()
|
||||||
|
let groups = document.getElementById("report_groups").value.trim()
|
||||||
|
let outtype = document.getElementById("report_output_type").value;
|
||||||
|
|
||||||
|
let report_url = `interface.php?action=generate_report_by_groups&gameid=${gameid}&groups=${groups}&filter=${filter}&outtype=${outtype}`;
|
||||||
|
report_url = report_url.replace("#", "%23");
|
||||||
|
window.open(report_url, "_blank");
|
||||||
|
}
|
||||||
|
|
||||||
// function hint_all_groups(target_element_id) {
|
// function hint_all_groups(target_element_id) {
|
||||||
// const hintbox_insert_fn = (record) => {
|
// const hintbox_insert_fn = (record) => {
|
||||||
// let targetF = document.getElementById(target_element_id);
|
// let targetF = document.getElementById(target_element_id);
|
||||||
|
@ -140,11 +140,17 @@ function generate_report() {
|
|||||||
let report_display = statsTab.document.getElementById("report_display");
|
let report_display = statsTab.document.getElementById("report_display");
|
||||||
report_display.innerHTML = "";
|
report_display.innerHTML = "";
|
||||||
|
|
||||||
|
let ch_n = 0;
|
||||||
stats.forEach((challenge) => {
|
stats.forEach((challenge) => {
|
||||||
let challenge_box = document.createElement("section");
|
let challenge_box = document.createElement("section");
|
||||||
challenge_box.classList.add("challenge");
|
challenge_box.classList.add("challenge");
|
||||||
challenge_box.style.width = "100%";
|
challenge_box.style.width = "100%";
|
||||||
|
|
||||||
|
let seq_num = document.createElement("section");
|
||||||
|
seq_num.classList.add("seq-num");
|
||||||
|
seq_num.innerText = ++ch_n;
|
||||||
|
challenge_box.append(seq_num);
|
||||||
|
|
||||||
let img_url = challenge["image_url"];
|
let img_url = challenge["image_url"];
|
||||||
if (img_url !== "") {
|
if (img_url !== "") {
|
||||||
let fig = document.createElement("img");
|
let fig = document.createElement("img");
|
||||||
|
@ -1 +0,0 @@
|
|||||||
Digit kvíz I-III eredmények kurzusonként
|
|
@ -108,7 +108,7 @@
|
|||||||
|
|
||||||
\begin{document}
|
\begin{document}
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\Huge \IfFileExists{stats/title.tex}{\input{stats/title.tex}}{Kvíz eredmények}
|
\Huge \IfFileExists{stats/title.tex}{\input{title.tex}}{Kvíz eredmények}
|
||||||
\end{center}
|
\end{center}
|
||||||
|
|
||||||
\Large
|
\Large
|
||||||
@ -116,6 +116,6 @@
|
|||||||
\normalsize
|
\normalsize
|
||||||
\clearpage
|
\clearpage
|
||||||
|
|
||||||
\inputIfFileExists{stats/content.tex}
|
\inputIfFileExists{content.tex}
|
||||||
|
|
||||||
\end{document}
|
\end{document}
|
Loading…
x
Reference in New Issue
Block a user