diff --git a/class/TestMgr.php b/class/TestMgr.php
index e4729d1..46dce6e 100644
--- a/class/TestMgr.php
+++ b/class/TestMgr.php
@@ -18,7 +18,8 @@ class TestSummary
private float $percentage; // Ratio of correct answers
// Calculate percentage.
- private function calculatePercentage() : void {
+ private function calculatePercentage(): void
+ {
if ($this->challengeN > 0) {
$this->percentage = $this->correctAnswerN / (double)$this->challengeN * 100.0;
} else { // avoid division by zero
@@ -288,12 +289,12 @@ class Test extends AutoStoring
return $this->gameId;
}
- public function isConcluded() : bool
+ public function isConcluded(): bool
{
return $this->state === self::TEST_CONCLUDED;
}
- public function isOngoing() : bool
+ public function isOngoing(): bool
{
return $this->state === self::TEST_ONGOING;
}
@@ -332,25 +333,28 @@ class TestMgr
}
// Update test in the database.
- function updateTest(Test &$test): void {
+ function updateTest(Test &$test): void
+ {
$a = $test->toArray();
$this->db->update($a);
}
// Add test to the database.
- function addTest(Game &$game, User &$user): Test {
+ function addTest(Game &$game, User &$user): Test
+ {
// create new test
- $test = new Test($this,$game, $user);
+ $test = new Test($this, $game, $user);
// insert into database
$a = $test->toArray(["_id"]);
$a = $this->db->insert($a);
- $test = new Test($this,$a);
+ $test = new Test($this, $a);
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
$fetch_criteria = [["gameid", "=", (int)$game->getId()], "AND", ["nickname", "=", $user->getNickname()]];
$previous_tests = $this->db->findBy($fetch_criteria);
@@ -384,12 +388,14 @@ class TestMgr
}
// Delete test from the database.
- function deleteTest(string $testid): void {
+ function deleteTest(string $testid): void
+ {
$this->db->deleteById($testid);
}
// 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]];
$test_data_array = $this->db->findBy($fetch_criteria);
$tests = [];
@@ -489,8 +495,10 @@ class TestMgr
$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]++;
+ if (trim($challenge["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]++;
+ }
}
}
@@ -514,7 +522,8 @@ class TestMgr
}
// Upgrade test. Just load tests and save them.
- function upgradeTests(array $ids = []) : void {
+ function upgradeTests(array $ids = []): void
+ {
$a = [];
if ($ids === []) {
$a = $this->db->findAll();
@@ -529,10 +538,11 @@ class TestMgr
}
// 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()]];
if ($ongoingOnly) {
- $query = [ ...$query, "AND", ["state", "=", TEST_ONGOING]];
+ $query = [...$query, "AND", ["state", "=", TEST_ONGOING]];
}
$qb = $this->db->createQueryBuilder();
diff --git a/game_manager_frame.php b/game_manager_frame.php
index a74a687..1faeef3 100644
--- a/game_manager_frame.php
+++ b/game_manager_frame.php
@@ -116,7 +116,8 @@ if (!get_autologin_state() || (($user_data["privilege"] !== PRIVILEGE_CREATOR) &
-
+
+
|
@@ -138,6 +139,19 @@ if (!get_autologin_state() || (($user_data["privilege"] !== PRIVILEGE_CREATOR) &
+
+
diff --git a/globals.php b/globals.php
index 47893d3..ea0e4e9 100644
--- a/globals.php
+++ b/globals.php
@@ -16,8 +16,10 @@ const MAIN_URL = "main.php";
const SESSION_NAME = "spreadquiz_sid";
const MAINTENANCE_FLAG_FILE = "MAINTENANCE";
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);
@@ -34,6 +36,7 @@ function init_datadir() {
mkdir(DATADIR);
mkdir(GAMEMEDIA_DIR);
mkdir(REPORT_DIR);
+ mkdir(REPORT_PDF_OUTPUT_DIR);
}
}
diff --git a/interface.php b/interface.php
index 79e3384..5d76a84 100644
--- a/interface.php
+++ b/interface.php
@@ -533,16 +533,35 @@ function generate_report_by_groups(ReqHandler &$rh, array $params): string
$gameid = trim($params["gameid"]);
$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");
+ // only PDF and TEX are valid
+ if (!in_array($outtype, ["pdf", "tex"])) {
+ return "FAIL";
+ }
+
$game = $gameMgr->getGame($gameid);
// verify game and access
- if (($game === null) || (!$game->isUserContributorOrOwner($user->getNickname()) || !$user->hasQuizmasterPrivilege())) {
+ if (($game === null) || ((!$game->isUserContributorOrOwner($user->getNickname()) && !$user->hasQuizmasterPrivilege()))) {
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
$report = new Report($game->getName());
foreach ($groups as $groupname) {
@@ -552,28 +571,39 @@ function generate_report_by_groups(ReqHandler &$rh, array $params): string
}
// generate latex
- $report_dir = "report";
- $content_dir = $report_dir . DIRECTORY_SEPARATOR . "stats";
- $report->saveTeX($content_dir);
+ $report->saveTeX($buildDir);
- $output = "";
- $contentType = "";
if ($outtype === "pdf") {
// run LuaLaTeX twice
- chdir($report_dir);
- $tex_cmd = "lualatex -interaction=nonstopmode report.tex";
+ chdir($buildDir);
+ $tex_cmd = TEX_ENGINE . " -interaction=nonstopmode report.tex";
exec($tex_cmd);
exec($tex_cmd);
- $output = "report.pdf";
+ // rename output
+ $origOutput = "report.pdf";
+ $output = $repId . ".pdf";
+ rename($origOutput, $output);
+
$contentType = "application/pdf";
- } else {
- $output = $content_dir . DIRECTORY_SEPARATOR . "content.tex";
- $contentType = "text/plain";
+ } else if ($outtype === "tex") {
+ $output = $buildDir . DIRECTORY_SEPARATOR . $repId . ".zip";
+ $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-Length: " . filesize($output));
+ header("Content-Disposition: attachment; filename=" . basename($output));
// deploy the generated PDF
$f = fopen($output, "r");
diff --git a/js/gamemgr.js b/js/gamemgr.js
index 7ddf486..1404720 100644
--- a/js/gamemgr.js
+++ b/js/gamemgr.js
@@ -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) {
// const hintbox_insert_fn = (record) => {
// let targetF = document.getElementById(target_element_id);
diff --git a/js/result_analyzer.js b/js/result_analyzer.js
index 513b7ff..58ac09b 100644
--- a/js/result_analyzer.js
+++ b/js/result_analyzer.js
@@ -140,11 +140,17 @@ function generate_report() {
let report_display = statsTab.document.getElementById("report_display");
report_display.innerHTML = "";
+ let ch_n = 0;
stats.forEach((challenge) => {
let challenge_box = document.createElement("section");
challenge_box.classList.add("challenge");
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"];
if (img_url !== "") {
let fig = document.createElement("img");
diff --git a/report/stats/title.tex b/report/stats/title.tex
deleted file mode 100644
index 147f3d5..0000000
--- a/report/stats/title.tex
+++ /dev/null
@@ -1 +0,0 @@
-Digit kvíz I-III eredmények kurzusonként
\ No newline at end of file
diff --git a/report/progressbar.sty b/report/template/progressbar.sty
similarity index 100%
rename from report/progressbar.sty
rename to report/template/progressbar.sty
diff --git a/report/report.tex b/report/template/report.tex
similarity index 92%
rename from report/report.tex
rename to report/template/report.tex
index 52d7721..bef3bad 100644
--- a/report/report.tex
+++ b/report/template/report.tex
@@ -108,7 +108,7 @@
\begin{document}
\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}
\Large
@@ -116,6 +116,6 @@
\normalsize
\clearpage
-\inputIfFileExists{stats/content.tex}
+\inputIfFileExists{content.tex}
\end{document}