238 lines
6.1 KiB
PHP
238 lines
6.1 KiB
PHP
<?php
|
|
|
|
require_once "GroupMgr.php";
|
|
require_once "TestMgr.php";
|
|
|
|
require_once "common_func.php";
|
|
|
|
class ReportBuilder
|
|
{
|
|
static public function getStatsByFilters(int $gameid, string $filter, string $groups, bool $bestOnly)
|
|
{
|
|
$groupMgr = new GroupMgr();
|
|
$testMgr = new TestMgr();
|
|
|
|
// get nicknames
|
|
$groups = explode_list($groups);
|
|
$nicknames = $groupMgr->getJoinedMembersByUniqueNames($groups);
|
|
$groupFilter = ["nickname", "IN", $nicknames];
|
|
|
|
// get IDs
|
|
$tests = $testMgr->getResultsByGameId($gameid, $filter, "", true, $bestOnly, $groupFilter);
|
|
$ids = array_map(fn($test) => $test["_id"], $tests);
|
|
|
|
// generate stats
|
|
return $testMgr->generateDetailedStats($gameid, $ids);
|
|
}
|
|
}
|
|
|
|
class TeXUtils
|
|
{
|
|
static public function processCodeInserts(string $str) : string
|
|
{
|
|
$parts = explode("`", $str);
|
|
$output = "";
|
|
for ($i = 0; $i < count($parts); $i++) {
|
|
$output .= $parts[$i];
|
|
if ($i < count($parts) - 1) {
|
|
if (($i % 2) === 0) {
|
|
$output .= "\\texttt{";
|
|
} else {
|
|
$output .= "}";
|
|
}
|
|
}
|
|
}
|
|
return $output;
|
|
}
|
|
|
|
static public function escape(string $str) : string {
|
|
return preg_replace("/([$#%&_~^])/", "\\\\$1", $str);
|
|
}
|
|
}
|
|
|
|
class Answer
|
|
{
|
|
const CORRECT = "Correct";
|
|
const INCORRECT = "Incorrect";
|
|
|
|
private string $text; // Answer's text
|
|
private string $type; // Answer's type
|
|
private float $ratio; // Answer's ratio
|
|
|
|
public function __construct(string $type, string $text, float $ratio)
|
|
{
|
|
$this->type = $type;
|
|
$this->text = $text;
|
|
$this->ratio = $ratio;
|
|
}
|
|
|
|
// Convert answer into TeX format.
|
|
public function genTeX(): string
|
|
{
|
|
return "\\answer" . $this->type . "{" . $this->ratio . "}{" . $this->text . "}\n";
|
|
}
|
|
|
|
// Get answer ratio.
|
|
public function getRatio(): float
|
|
{
|
|
return $this->ratio;
|
|
}
|
|
}
|
|
|
|
class Challenge
|
|
{
|
|
private string $question;
|
|
private array $answers;
|
|
private int $fillCount;
|
|
private int $skipCount;
|
|
|
|
// Sort answers by ratio.
|
|
private function sortAnswers() : void {
|
|
usort($this->answers, function($a, $b) {return $a->getRatio() < $b->getRatio();});
|
|
}
|
|
|
|
function __construct(array $data)
|
|
{
|
|
$this->question = $data["question"]; // store question
|
|
for ($i = 0; $i < count($data["answers"]); $i++) {
|
|
$answer = $data["answers"][$i];
|
|
$ratio = $data["answer_ratio"][$i];
|
|
$type = $answer === $data["correct_answer"] ? Answer::CORRECT : Answer::INCORRECT;
|
|
$this->answers[] = new Answer($type, $answer, $ratio);
|
|
}
|
|
$this->fillCount = array_sum($data["player_answers"]); // get fill count
|
|
$this->skipCount = $data["skipped"];
|
|
|
|
$this->sortAnswers(); // sort answers by fill ratio
|
|
}
|
|
|
|
public function getQuestion(): string
|
|
{
|
|
return $this->question;
|
|
}
|
|
|
|
public function getAnswers(): array
|
|
{
|
|
return $this->answers;
|
|
}
|
|
|
|
public function getFillCount(): int {
|
|
return $this->fillCount;
|
|
}
|
|
|
|
public function getSkipCount(): int {
|
|
return $this->skipCount;
|
|
}
|
|
|
|
public function getSubmissionCount() : int {
|
|
return $this->fillCount + $this->skipCount;
|
|
}
|
|
|
|
// Generate TeX representation.
|
|
public function genTeX(): string
|
|
{
|
|
$tex = "\\begin{question}{" . $this->question . "}{" . $this->fillCount . "}\n";
|
|
foreach ($this->answers as &$answer) {
|
|
$tex .= $answer->genTeX();
|
|
}
|
|
$tex .= "\\end{question}\n";
|
|
return $tex;
|
|
}
|
|
}
|
|
|
|
class ReportSection
|
|
{
|
|
private string $title;
|
|
private array $challenges;
|
|
|
|
private function getNumberOfSubmissions() : int {
|
|
return count($this->challenges) > 0 ? $this->challenges[0]->getSubmissionCount() : 0;
|
|
}
|
|
|
|
function __construct(string $title, array $challenges)
|
|
{
|
|
$this->title = $title;
|
|
$this->challenges = array_map(fn($ch) => new Challenge($ch), $challenges);
|
|
}
|
|
|
|
function getChallenges(): array
|
|
{
|
|
return $this->challenges;
|
|
}
|
|
|
|
function getTitle(): string
|
|
{
|
|
return $this->title;
|
|
}
|
|
|
|
// Generate TeX representation of this report.
|
|
function genTeX(): string
|
|
{
|
|
$tex = "\\begin{quiz}{" . $this->title . "}{" . $this->getNumberOfSubmissions() . "}\n";
|
|
foreach ($this->challenges as $challenge) {
|
|
$tex .= $challenge->genTeX();
|
|
}
|
|
$tex .= "\\end{quiz}\n";
|
|
return $tex;
|
|
}
|
|
}
|
|
|
|
class Report
|
|
{
|
|
private string $title;
|
|
private array $sections;
|
|
private array $comments;
|
|
|
|
function __construct(string $title)
|
|
{
|
|
$this->title = $title;
|
|
$this->sections = [];
|
|
$this->comments = [];
|
|
}
|
|
|
|
|
|
// Add a new section to the report.
|
|
function addSection(ReportSection &$section): void
|
|
{
|
|
$this->sections[] = $section;
|
|
}
|
|
|
|
function addComment(string $comment): void
|
|
{
|
|
$this->comments[] = $comment;
|
|
}
|
|
|
|
// Generate TeX representation.
|
|
function genTeX(): string
|
|
{
|
|
$tex = "";
|
|
// generate content
|
|
foreach ($this->sections as $section) {
|
|
$tex .= $section->genTeX() . "\n\n";
|
|
}
|
|
|
|
// add comments if any
|
|
if ($this->comments != []) {
|
|
$tex .= "\\huge\\noindent A következő oldal érzékeny információkat tartalmazhat!\\newline Nem publikálandó!\n\\clearpage\n\n";
|
|
$tex .= "\\clearpage\n\small\n\\noindent ";
|
|
$tex .= join("\\\\\n", $this->comments);
|
|
}
|
|
|
|
// escape LaTeX control characters
|
|
$tex = TeXUtils::processCodeInserts($tex);
|
|
$tex = TeXUtils::escape($tex);
|
|
return $tex;
|
|
}
|
|
|
|
const CONTENT_FILE = "content.tex";
|
|
const TITLE_FILE = "title.tex";
|
|
|
|
// Save TeX representation.
|
|
function saveTeX(string $dir): void
|
|
{
|
|
file_put_contents($dir . DIRECTORY_SEPARATOR . self::CONTENT_FILE, $this->genTeX());
|
|
file_put_contents($dir . DIRECTORY_SEPARATOR . self::TITLE_FILE, $this->title);
|
|
}
|
|
}
|
|
|