- 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
|
||||
|
||||
// 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();
|
||||
|
@ -116,7 +116,8 @@ if (!get_autologin_state() || (($user_data["privilege"] !== PRIVILEGE_CREATOR) &
|
||||
</td>
|
||||
<td>
|
||||
<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>
|
||||
</tr>
|
||||
</table>
|
||||
@ -138,6 +139,19 @@ if (!get_autologin_state() || (($user_data["privilege"] !== PRIVILEGE_CREATOR) &
|
||||
</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>
|
||||
list_all_games();
|
||||
</script>
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -1 +0,0 @@
|
||||
Digit kvíz I-III eredmények kurzusonként
|
@ -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}
|
Loading…
x
Reference in New Issue
Block a user