<?php
declare(strict_types=1);
namespace App\Entity\Core\Quiz;
use App\Entity\Core\Classroom\Classroom;
use App\Entity\Core\ActivitySessionInterface;
use App\Entity\Core\FolderAwareInterface;
use App\Entity\Core\FolderAwareTrait;
use App\Entity\Core\SelfStudy\SelfStudyExamQuiz;
use App\Entity\Core\SelfStudy\SelfStudyTopic;
use App\Entity\Core\SelfStudy\SelfStudyTypicalExamProblem;
use App\Entity\Core\TemplateMathproblem;
use App\Entity\User\User;
use DateTime;
use DateTimeZone;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
use Exception;
use JsonSerializable;
#[ORM\Table('quiz_session')]
#[ORM\Entity(repositoryClass: QuizSessionRepository::class)]
class QuizSession implements JsonSerializable, FolderAwareInterface, ActivitySessionInterface
{
use FolderAwareTrait;
private const FOLDER_ACTIVITY_TYPE = 'quiz';
/**
* @var int
*/
#[ORM\Column(name: 'id', type: 'integer')]
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'AUTO')]
private int $id;
/**
* @var Quiz
*/
#[ORM\JoinColumn(name: 'quiz', referencedColumnName: 'id', onDelete: 'CASCADE', nullable: true)]
#[ORM\ManyToOne(targetEntity: Quiz::class, inversedBy: 'quizSessions')]
private ?Quiz $quiz;
/**
* @var User
*/
#[ORM\JoinColumn(name: 'created_by', referencedColumnName: 'id')]
#[ORM\ManyToOne(targetEntity: User::class)]
private ?User $createdBy;
/**
* @var Classroom
*/
#[ORM\JoinColumn(name: 'classroom', referencedColumnName: 'id', onDelete: 'CASCADE')]
#[ORM\ManyToOne(targetEntity: Classroom::class, inversedBy: 'quizSessions')]
private ?Classroom $classroom;
/**
* @var bool
*/
#[ORM\Column(name: 'dormant', type: 'boolean', nullable: true)]
private ?bool $dormant;
/**
* @var int
*/
#[ORM\Column(type: 'smallint', nullable: true)]
private ?int $duration;
/**
* @var DateTime|null
*/
#[ORM\Column(name: 'startTime', type: 'datetime', nullable: true)]
private ?DateTime $startTime;
#[ORM\Column(type: 'datetime', nullable: true)]
private ?DateTime $deadline;
#[ORM\Column(type: 'string', length: 255, nullable: true)]
private ?string $name;
/**
* @var DateTime
*/
#[ORM\Column(name: 'createdAt', type: 'datetime', nullable: false)]
private DateTime $createdAt;
/**
* @var integer
* [0 - created, in phase of editing; 1 - ready to start, if it's dormant ; 2 - startTime set]
*/
#[ORM\Column(type: 'integer', nullable: false, options: ['default' => 0])]
private int $status = 0;
/**
* @var integer
*/
#[ORM\Column(type: 'integer', nullable: false, options: ['default' => 1])]
private int $isEnabled = 1;
#[ORM\Column(type: 'boolean', nullable: true, options: ['default' => false])]
private ?bool $deleted = false;
/**
* @var QuizUnit[]
*/
#[ORM\OneToMany(targetEntity: QuizUnit::class, mappedBy: 'quizSession', cascade: ['persist'])]
private $quizUnits;
/**
* @var bool
*/
#[ORM\Column(type: 'boolean', nullable: true)]
private ?bool $durationIgnored;
/**
* @var bool
*/
#[ORM\Column(name: 'hints_available', type: 'boolean', nullable: true)]
private ?bool $hintsAvailable;
#[ORM\Column(name: 'results_available_before_deadline', type: 'boolean', nullable: true)]
private ?bool $resultsAvailableBeforeDeadline;
/**
* @var string|null
*/
#[ORM\Column(name: 'isbn', type: 'string', length: 255, nullable: true)]
private ?string $isbn;
/**
* @var DateTime
*/
#[ORM\Column(name: 'updated_at', type: 'datetime', nullable: true)]
private DateTime $updatedAt;
/**
* @var User
*/
#[ORM\JoinColumn(name: 'updated_by', referencedColumnName: 'id')]
#[ORM\ManyToOne(targetEntity: User::class)]
private User $updatedBy;
/**
* @var string|null
*/
#[ORM\Column(name: 'description', type: 'text', nullable: true)]
private ?string $description;
/**
* @var DateTime
*/
#[ORM\Column(name: 'result_view_open_at', type: 'datetime', nullable: true)]
private ?DateTime $resultViewOpenAt;
/**
* @var ?bool
*/
#[ORM\Column(name: 'extra_teachers', type: 'boolean', nullable: true)]
private ?bool $extraTeachers = null;
/**
* possible values: 'self_study_exam', 'self_study_topic', 'self_study_typical_problem'
* If it is null then it is a regular quiz
*/
#[ORM\Column(name: 'type', type: 'string', length: 255, nullable: true, options: ['default' => ''])]
private ?string $type = '';
/**
* @var SelfStudyTopic
*/
#[ORM\JoinColumn(name: 'self_study_topic', referencedColumnName: 'id')]
#[ORM\ManyToOne(targetEntity: SelfStudyTopic::class)]
private $selfStudyTopic = null;
/**
* @var SelfStudyTypicalExamProblem
*/
#[ORM\JoinColumn(name: 'self_study_typical_exam_problem', referencedColumnName: 'id')]
#[ORM\ManyToOne(targetEntity: SelfStudyTypicalExamProblem::class)]
private $selfStudyTypicalExamProblem = null;
/**
* @var SelfStudyExamQuiz
*/
#[ORM\JoinColumn(name: 'self_study_exam', referencedColumnName: 'id')]
#[ORM\ManyToOne(targetEntity: SelfStudyExamQuiz::class)]
private $selfStudyExam = null;
/**
* @var bool
*/
#[ORM\Column(name: 'deactivated_questions', type: 'boolean', nullable: true)]
private ?bool $deactivatedProblems = null;
/**
* @var bool
*/
#[ORM\Column(name: 'is_archived', type: 'boolean', nullable: true)]
private ?bool $isArchived = null;
/**
* @var int|null
*/
#[ORM\Column(name: 'copy_of', type: 'integer', nullable: true)]
private ?int $copyOfSession = null;
/**
* @var bool|null
*/
#[ORM\Column(name: 'is_shared_quiz', type: 'boolean', nullable: true)]
private ?bool $isSharedQuiz = null;
/**
* @var bool|null
*/
#[ORM\Column(name: 'is_pinned', type: 'boolean', nullable: true)]
private ?bool $isPinned = null;
#[ORM\Column(name: 'can_pause', type: 'boolean', options: ['default' => false])]
private bool $canPause = false;
/**
*/
public function __construct(?int $duration)
{
$this->createdAt = new DateTime('now', new DateTimeZone('Europe/Copenhagen'));
$this->updatedAt = new DateTime('now', new DateTimeZone('Europe/Copenhagen'));
$this->quizUnits = new ArrayCollection();
$this->duration = $duration;
}
public function getCopyOfSession(): ?int
{
return $this->copyOfSession;
}
public function setCopyOfSession(?int $copyOfSession): void
{
$this->copyOfSession = $copyOfSession;
}
public function getId(): int
{
return $this->id;
}
/**
* @return Quiz
*/
public function getQuiz(): ?Quiz
{
return $this->quiz;
}
/**
* @param Quiz $quiz
* @return $this
*/
public function setQuiz(?Quiz $quiz): static
{
$this->quiz = $quiz;
return $this;
}
/**
* @return User
*/
public function getCreatedBy(): ?User
{
return $this->createdBy;
}
/**
* @param User $createdBy
* @return $this
*/
public function setCreatedBy(?User $createdBy): static
{
$this->createdBy = $createdBy;
return $this;
}
/**
* @return Classroom
*/
public function getClassroom(): ?Classroom
{
return $this->classroom;
}
/**
* @param Classroom $classroom
* @return $this
*/
public function setClassroom(?Classroom $classroom)
{
$this->classroom = $classroom;
return $this;
}
/**
* @return boolean
*/
public function isDormant(): ?bool
{
return $this->dormant;
}
/**
* @param boolean $dormant
* @return $this
*/
public function setDormant(?bool $dormant)
{
$this->dormant = $dormant;
return $this;
}
/**
* @return DateTime
*/
public function getStartTime(): ?DateTime
{
return $this->startTime;
}
/**
* @param DateTime $startTime
* @return $this
*/
public function setStartTime(?DateTime $startTime)
{
$this->startTime = $startTime;
return $this;
}
public function getCreatedAt(): DateTime
{
return $this->createdAt;
}
public function setCreatedAt(DateTime $createdAt): void
{
$this->createdAt = $createdAt;
}
public function getStatus(): int
{
return $this->status;
}
/**
* @return $this
*/
public function setStatus(int $status): static
{
$this->status = $status;
return $this;
}
/**
* @return QuizUnit[]
*/
public function getQuizUnits()
{
return $this->quizUnits;
}
/**
* @param QuizUnit[] $quizUnits
* @return $this
*/
public function setQuizUnits($quizUnits)
{
$this->quizUnits = $quizUnits;
return $this;
}
/**
* @return $this
*/
public function addQuizUnit(QuizUnit $quizUnit)
{
$quizUnit->setQuizSession($this);
$this->quizUnits->add($quizUnit);
return $this;
}
public function getEndTime()
{
if ($this->getDeadline() !== null) {
return $this->getDeadline();
}
$endTime = clone $this->startTime;
return $endTime->modify(sprintf('+%d minutes', $this->getDuration()));
}
public function setIsEnabled(int $isEnabled): void
{
$this->isEnabled = $isEnabled;
}
public function getIsEnabled(): int
{
return $this->isEnabled;
}
public function isDeleted(): ?bool
{
return $this->deleted;
}
public function setDeleted(bool $deleted): void
{
$this->deleted = $deleted;
}
public function getDeadline(): ?DateTime
{
return $this->deadline;
}
public function setDeadline(?DateTime $deadline = null): void
{
$this->deadline = $deadline;
}
public function isDurationIgnored(): ?bool
{
return $this->durationIgnored;
}
public function setDurationIgnored(bool $durationIgnored): void
{
$this->durationIgnored = $durationIgnored;
}
public function getDuration(): ?int
{
return $this->duration;
}
public function setDuration(?int $duration): void
{
$this->duration = $duration;
}
public function isHintsAvailable(): ?bool
{
return $this->hintsAvailable;
}
public function setHintsAvailable(bool $hintsAvailable): void
{
$this->hintsAvailable = $hintsAvailable;
}
public function areResultsAvailableBeforeDeadline(): bool
{
return (bool)$this->resultsAvailableBeforeDeadline;
}
public function setResultsAvailableBeforeDeadline(?bool $resultsAvailableBeforeDeadline): void
{
$this->resultsAvailableBeforeDeadline = $resultsAvailableBeforeDeadline;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(?string $name): void
{
$this->name = $name;
}
public function isHomework(): bool
{
return (bool)$this->deadline;
}
public function getType(): string
{
return $this->type;
}
public function isSelfStudyExamQuiz(): bool
{
return $this->type === 'self_study_exam';
}
public function isSelfStudyTopicQuiz(): bool
{
return $this->type === 'self_study_topic';
}
public function isSelfStudyTypicalProblemQuiz(): bool
{
return $this->type === 'self_study_typical_exam_problem';
}
public function isSelfStudyQuiz(): bool
{
return $this->isSelfStudyExamQuiz() || $this->isSelfStudyTopicQuiz() || $this->isSelfStudyTypicalProblemQuiz();
}
public function setTypeToSelfStudyExamQuiz(): void
{
$this->type = 'self_study_exam';
}
public function setTypeToSelfStudyTopicQuiz(): void
{
$this->type = 'self_study_topic';
}
public function setTypeToSelfStudyTypicalProblemQuiz(): void
{
$this->type = 'self_study_typical_problem';
}
public function setType(?string $type): void
{
$this->type = $type;
}
public function getSelfStudyTopic(): ?SelfStudyTopic
{
return $this->selfStudyTopic;
}
public function setSelfStudyTopic(?SelfStudyTopic $selfStudyTopic): void
{
$this->selfStudyTopic = $selfStudyTopic;
}
public function getSelfStudyTypicalExamProblem(): ?SelfStudyTypicalExamProblem
{
return $this->selfStudyTypicalExamProblem;
}
public function setSelfStudyTypicalExamProblem(?SelfStudyTypicalExamProblem $selfStudyTypicalExamProblem): void
{
$this->selfStudyTypicalExamProblem = $selfStudyTypicalExamProblem;
}
public function getSelfStudyExam(): ?SelfStudyExamQuiz
{
return $this->selfStudyExam;
}
public function setSelfStudyExam(?SelfStudyExamQuiz $selfStudyExam): void
{
$this->selfStudyExam = $selfStudyExam;
}
/**
* @throws Exception
*/
public function jsonSerialize(): mixed
{
$topics = [];
$quiz = $this->getQuiz();
if ($quiz) {
foreach ($quiz->getSortedMathproblems() as $problem) {
$template = $problem->getTemplate();
if (!$template) {
continue;
}
$this->getUniqueTopics($template, $topics);
}
$topics = array_values($topics);
}
return [
"duration" => $this->getDuration(),
"id" => $this->getId(),
"name" => $this->prefixNameWithFolderSortOrder($this->getName(), self::FOLDER_ACTIVITY_TYPE, $this->getQuiz()?->getId()),
"startTime" => $this->getStartTime(),
'deadline' => $this->getDeadline() ?? $this->getEndTime(),
'isHomework' => $this->isHomework(),
'secondsUntilDeadline' => $this->getSecondsUntilDeadline(),
'durationIgnored' => $this->isDurationIgnored(),
'session_type' => basename(str_replace('\\', '/', __CLASS__)),
'activity_type' => 'quiz',
'start' => $this->getStartTime()->getTimestamp(),
'topics' => $topics
];
}
public function getSortOrderInFolder(): ?int
{
return $this->getFolderSortOrder(self::FOLDER_ACTIVITY_TYPE, $this->getQuiz()?->getId());
}
public function setISBN(?string $isbn = null): void
{
$this->isbn = $isbn;
}
public function getISBN(): ?string
{
return $this->isbn;
}
public function getUpdatedAt(): DateTime
{
return $this->updatedAt;
}
public function setUpdatedAt(DateTime $updatedAt): void
{
$this->updatedAt = $updatedAt;
}
public function getUpdatedBy(): User
{
return $this->updatedBy;
}
public function setUpdatedBy(User $updatedBy): void
{
$this->updatedBy = $updatedBy;
}
public function getDescription(): string
{
return $this->description;
}
public function setDescription(string $description): void
{
$this->description = $description;
}
public function getResultViewOpenAt(): ?DateTime
{
return $this->resultViewOpenAt;
}
public function setResultViewOpenAt(?DateTime $resultViewOpenAt): void
{
$this->resultViewOpenAt = $resultViewOpenAt;
}
public function hasExtraTeachers(): ?bool
{
return $this->extraTeachers;
}
public function setExtraTeachers(?bool $extraTeachers): void
{
$this->extraTeachers = $extraTeachers;
}
public function hasDeactivatedProblems(): ?bool
{
return $this->deactivatedProblems;
}
public function setDeactivatedProblems(?bool $deactivatedProblems): void
{
$this->deactivatedProblems = $deactivatedProblems;
}
public function isArchived(): ?bool
{
return $this->isArchived;
}
public function setIsArchived(?bool $isArchived): void
{
$this->isArchived = $isArchived;
}
public function isSharedQuiz(): ?bool
{
return $this->isSharedQuiz;
}
public function setIsSharedQuiz(bool $isSharedQuiz): void
{
$this->isSharedQuiz = $isSharedQuiz;
}
public function getIsPinned(): ?bool
{
return $this->isPinned;
}
public function setIsPinned(?bool $isPinned): void
{
$this->isPinned = $isPinned;
}
public function getNameOrDefaultToQuizName(): ?string
{
return trim($this->getName() ?? '') === '' ? $this->getQuiz()->getName() : $this->getName();
}
public function getCanPause(): bool
{
return $this->canPause;
}
public function canPause(): bool
{
return $this->canPause;
}
/**
* @param bool $canPause
*/
public function setCanPause(?bool $canPause): void
{
$this->canPause = $canPause;
}
/**
* @throws Exception
*/
public function getSecondsUntilDeadline():int
{
if($this->getDeadline() === null){
return 0;
}
$now = new DateTime('now', new DateTimeZone('Europe/Copenhagen'));
$interval = $now->diff($this->getDeadline());
$seconds = ($interval->days * 24 * 60 * 60) + ($interval->h * 60 * 60) + ($interval->i * 60) + $interval->s;
if($interval->invert){
return 0;
}
return $seconds;
}
public function getProblemCount(){
return $this->quiz->getProblemCount();
}
private function getUniqueTopics(TemplateMathproblem $template, array &$topics): void
{
if($topic = $template->getTopic()){
$topics[$topic->getId()] = $topic->jsonSerialize();
}
}
}