|
|
@ -1,9 +1,9 @@ |
|
|
|
<?php |
|
|
|
<?php |
|
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* For licensing terms, see /license.txt */ |
|
|
|
/* For licensing terms, see /license.txt */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1); |
|
|
|
|
|
|
|
|
|
|
|
namespace Chamilo\CoreBundle\Entity; |
|
|
|
namespace Chamilo\CoreBundle\Entity; |
|
|
|
|
|
|
|
|
|
|
|
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter; |
|
|
|
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter; |
|
|
@ -22,61 +22,102 @@ use Symfony\Component\Validator\Constraints as Assert; |
|
|
|
/** |
|
|
|
/** |
|
|
|
* Questions per quiz user attempts. |
|
|
|
* Questions per quiz user attempts. |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
#[ApiResource(operations: [new Get(security: 'is_granted("VIEW", object)'), new GetCollection(security: 'is_granted("ROLE_USER")')], security: 'is_granted("ROLE_USER")', normalizationContext: ['groups' => ['track_e_attempt:read']])] |
|
|
|
#[ApiResource( |
|
|
|
|
|
|
|
operations: [ |
|
|
|
|
|
|
|
new Get(security: 'is_granted("VIEW", object)'), |
|
|
|
|
|
|
|
new GetCollection(security: 'is_granted("ROLE_USER")'), |
|
|
|
|
|
|
|
], |
|
|
|
|
|
|
|
normalizationContext: [ |
|
|
|
|
|
|
|
'groups' => ['track_e_attempt:read'], |
|
|
|
|
|
|
|
], |
|
|
|
|
|
|
|
security: 'is_granted("ROLE_USER")' |
|
|
|
|
|
|
|
)] |
|
|
|
#[ORM\Table(name: 'track_e_attempt')] |
|
|
|
#[ORM\Table(name: 'track_e_attempt')] |
|
|
|
#[ORM\Index(name: 'exe_id', columns: ['exe_id'])] |
|
|
|
#[ORM\Index(columns: ['exe_id'], name: 'exe_id')] |
|
|
|
#[ORM\Index(name: 'user_id', columns: ['user_id'])] |
|
|
|
#[ORM\Index(columns: ['user_id'], name: 'user_id')] |
|
|
|
#[ORM\Index(name: 'question_id', columns: ['question_id'])] |
|
|
|
#[ORM\Index(columns: ['question_id'], name: 'question_id')] |
|
|
|
#[ORM\Index(name: 'idx_track_e_attempt_tms', columns: ['tms'])] |
|
|
|
#[ORM\Index(columns: ['tms'], name: 'idx_track_e_attempt_tms')] |
|
|
|
#[ORM\Entity] |
|
|
|
#[ORM\Entity] |
|
|
|
#[ApiFilter(filterClass: SearchFilter::class, properties: ['user' => 'exact', 'questionId' => 'exact', 'answer' => 'exact', 'marks' => 'exact'])] |
|
|
|
#[ApiFilter( |
|
|
|
|
|
|
|
filterClass: SearchFilter::class, |
|
|
|
|
|
|
|
properties: [ |
|
|
|
|
|
|
|
'user' => 'exact', |
|
|
|
|
|
|
|
'questionId' => 'exact', |
|
|
|
|
|
|
|
'answer' => 'exact', |
|
|
|
|
|
|
|
'marks' => 'exact', |
|
|
|
|
|
|
|
] |
|
|
|
|
|
|
|
)] |
|
|
|
class TrackEAttempt |
|
|
|
class TrackEAttempt |
|
|
|
{ |
|
|
|
{ |
|
|
|
use UserTrait; |
|
|
|
use UserTrait; |
|
|
|
|
|
|
|
|
|
|
|
#[ORM\Column(name: 'id', type: 'integer')] |
|
|
|
#[ORM\Column(name: 'id', type: 'integer')] |
|
|
|
#[ORM\Id] |
|
|
|
#[ORM\Id] |
|
|
|
#[ORM\GeneratedValue(strategy: 'IDENTITY')] |
|
|
|
#[ORM\GeneratedValue(strategy: 'IDENTITY')] |
|
|
|
protected ?int $id = null; |
|
|
|
protected ?int $id = null; |
|
|
|
|
|
|
|
|
|
|
|
#[Assert\NotNull] |
|
|
|
#[Assert\NotNull] |
|
|
|
#[ORM\ManyToOne(targetEntity: \Chamilo\CoreBundle\Entity\TrackEExercise::class, inversedBy: 'attempts')] |
|
|
|
#[ORM\ManyToOne(targetEntity: TrackEExercise::class, inversedBy: 'attempts')] |
|
|
|
#[ORM\JoinColumn(name: 'exe_id', referencedColumnName: 'exe_id', nullable: false, onDelete: 'CASCADE')] |
|
|
|
#[ORM\JoinColumn(name: 'exe_id', referencedColumnName: 'exe_id', nullable: false, onDelete: 'CASCADE')] |
|
|
|
protected TrackEExercise $trackExercise; |
|
|
|
protected TrackEExercise $trackExercise; |
|
|
|
|
|
|
|
|
|
|
|
#[Assert\NotNull] |
|
|
|
#[Assert\NotNull] |
|
|
|
#[Groups(['track_e_attempt:read'])] |
|
|
|
#[Groups(['track_e_attempt:read'])] |
|
|
|
#[ORM\ManyToOne(targetEntity: \Chamilo\CoreBundle\Entity\User::class, inversedBy: 'trackEAttempts')] |
|
|
|
#[ORM\ManyToOne(targetEntity: User::class, inversedBy: 'trackEAttempts')] |
|
|
|
#[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id', onDelete: 'CASCADE')] |
|
|
|
#[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id', onDelete: 'CASCADE')] |
|
|
|
protected User $user; |
|
|
|
protected User $user; |
|
|
|
|
|
|
|
|
|
|
|
#[Assert\NotBlank] |
|
|
|
#[Assert\NotBlank] |
|
|
|
#[Groups(['track_e_attempt:read'])] |
|
|
|
#[Groups(['track_e_attempt:read'])] |
|
|
|
#[ORM\Column(name: 'question_id', type: 'integer', nullable: false)] |
|
|
|
#[ORM\Column(name: 'question_id', type: 'integer', nullable: false)] |
|
|
|
protected ?int $questionId = null; |
|
|
|
protected ?int $questionId = null; |
|
|
|
|
|
|
|
|
|
|
|
#[Groups(['track_e_attempt:read'])] |
|
|
|
#[Groups(['track_e_attempt:read'])] |
|
|
|
#[ORM\Column(name: 'answer', type: 'text', nullable: false)] |
|
|
|
#[ORM\Column(name: 'answer', type: 'text', nullable: false)] |
|
|
|
protected string $answer; |
|
|
|
protected string $answer; |
|
|
|
|
|
|
|
|
|
|
|
#[ORM\Column(name: 'teacher_comment', type: 'text', nullable: false)] |
|
|
|
#[ORM\Column(name: 'teacher_comment', type: 'text', nullable: false)] |
|
|
|
protected string $teacherComment; |
|
|
|
protected string $teacherComment; |
|
|
|
|
|
|
|
|
|
|
|
#[Groups(['track_e_attempt:read'])] |
|
|
|
#[Groups(['track_e_attempt:read'])] |
|
|
|
#[ORM\Column(name: 'marks', type: 'float', precision: 6, scale: 2, nullable: false)] |
|
|
|
#[ORM\Column(name: 'marks', type: 'float', precision: 6, scale: 2, nullable: false)] |
|
|
|
protected float $marks; |
|
|
|
protected float $marks; |
|
|
|
|
|
|
|
|
|
|
|
#[ORM\Column(name: 'position', type: 'integer', nullable: true)] |
|
|
|
#[ORM\Column(name: 'position', type: 'integer', nullable: true)] |
|
|
|
protected ?int $position = null; |
|
|
|
protected ?int $position = null; |
|
|
|
|
|
|
|
|
|
|
|
#[Assert\NotNull] |
|
|
|
#[Assert\NotNull] |
|
|
|
#[ORM\Column(name: 'tms', type: 'datetime', nullable: false)] |
|
|
|
#[ORM\Column(name: 'tms', type: 'datetime', nullable: false)] |
|
|
|
protected DateTime $tms; |
|
|
|
protected DateTime $tms; |
|
|
|
|
|
|
|
|
|
|
|
#[ORM\Column(name: 'filename', type: 'string', length: 255, nullable: true)] |
|
|
|
#[ORM\Column(name: 'filename', type: 'string', length: 255, nullable: true)] |
|
|
|
protected ?string $filename = null; |
|
|
|
protected ?string $filename = null; |
|
|
|
|
|
|
|
|
|
|
|
#[ORM\Column(name: 'seconds_spent', type: 'integer')] |
|
|
|
#[ORM\Column(name: 'seconds_spent', type: 'integer')] |
|
|
|
protected int $secondsSpent; |
|
|
|
protected int $secondsSpent; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* @var Collection|AttemptFile[] |
|
|
|
* @var Collection<int, AttemptFile> |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
#[ORM\OneToMany(targetEntity: \Chamilo\CoreBundle\Entity\AttemptFile::class, mappedBy: 'attempt', cascade: ['persist'], orphanRemoval: true)] |
|
|
|
#[ORM\OneToMany( |
|
|
|
|
|
|
|
mappedBy: 'attempt', |
|
|
|
|
|
|
|
targetEntity: AttemptFile::class, |
|
|
|
|
|
|
|
cascade: ['persist'], |
|
|
|
|
|
|
|
orphanRemoval: true |
|
|
|
|
|
|
|
)] |
|
|
|
protected Collection $attemptFiles; |
|
|
|
protected Collection $attemptFiles; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* @var Collection|AttemptFeedback[] |
|
|
|
* @var Collection<int, AttemptFeedback> |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
#[ORM\OneToMany(targetEntity: \Chamilo\CoreBundle\Entity\AttemptFeedback::class, mappedBy: 'attempt', cascade: ['persist'], orphanRemoval: true)] |
|
|
|
#[ORM\OneToMany( |
|
|
|
|
|
|
|
mappedBy: 'attempt', |
|
|
|
|
|
|
|
targetEntity: AttemptFeedback::class, |
|
|
|
|
|
|
|
cascade: ['persist'], |
|
|
|
|
|
|
|
orphanRemoval: true |
|
|
|
|
|
|
|
)] |
|
|
|
protected Collection $attemptFeedbacks; |
|
|
|
protected Collection $attemptFeedbacks; |
|
|
|
|
|
|
|
|
|
|
|
public function __construct() |
|
|
|
public function __construct() |
|
|
|
{ |
|
|
|
{ |
|
|
|
$this->attemptFiles = new ArrayCollection(); |
|
|
|
$this->attemptFiles = new ArrayCollection(); |
|
|
@ -84,187 +125,168 @@ class TrackEAttempt |
|
|
|
$this->teacherComment = ''; |
|
|
|
$this->teacherComment = ''; |
|
|
|
$this->secondsSpent = 0; |
|
|
|
$this->secondsSpent = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public function getQuestionId(): ?int |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return $this->questionId; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function setQuestionId(int $questionId): self |
|
|
|
public function setQuestionId(int $questionId): self |
|
|
|
{ |
|
|
|
{ |
|
|
|
$this->questionId = $questionId; |
|
|
|
$this->questionId = $questionId; |
|
|
|
|
|
|
|
|
|
|
|
return $this; |
|
|
|
return $this; |
|
|
|
} |
|
|
|
} |
|
|
|
/** |
|
|
|
|
|
|
|
* Get questionId. |
|
|
|
public function getAnswer(): string |
|
|
|
* |
|
|
|
|
|
|
|
* @return int |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public function getQuestionId() |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
return $this->questionId; |
|
|
|
return $this->answer; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function setAnswer(string $answer): self |
|
|
|
public function setAnswer(string $answer): self |
|
|
|
{ |
|
|
|
{ |
|
|
|
$this->answer = $answer; |
|
|
|
$this->answer = $answer; |
|
|
|
|
|
|
|
|
|
|
|
return $this; |
|
|
|
return $this; |
|
|
|
} |
|
|
|
} |
|
|
|
/** |
|
|
|
|
|
|
|
* Get answer. |
|
|
|
public function getTeacherComment(): string |
|
|
|
* |
|
|
|
|
|
|
|
* @return string |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public function getAnswer() |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
return $this->answer; |
|
|
|
return $this->teacherComment; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function setTeacherComment(string $teacherComment): self |
|
|
|
public function setTeacherComment(string $teacherComment): self |
|
|
|
{ |
|
|
|
{ |
|
|
|
$this->teacherComment = $teacherComment; |
|
|
|
$this->teacherComment = $teacherComment; |
|
|
|
|
|
|
|
|
|
|
|
return $this; |
|
|
|
return $this; |
|
|
|
} |
|
|
|
} |
|
|
|
/** |
|
|
|
|
|
|
|
* Get teacherComment. |
|
|
|
public function getMarks(): float |
|
|
|
* |
|
|
|
|
|
|
|
* @return string |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public function getTeacherComment() |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
return $this->teacherComment; |
|
|
|
return $this->marks; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function setMarks(float $marks): self |
|
|
|
public function setMarks(float $marks): self |
|
|
|
{ |
|
|
|
{ |
|
|
|
$this->marks = $marks; |
|
|
|
$this->marks = $marks; |
|
|
|
|
|
|
|
|
|
|
|
return $this; |
|
|
|
return $this; |
|
|
|
} |
|
|
|
} |
|
|
|
/** |
|
|
|
|
|
|
|
* Get marks. |
|
|
|
public function getPosition(): ?int |
|
|
|
* |
|
|
|
|
|
|
|
* @return float |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public function getMarks() |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
return $this->marks; |
|
|
|
return $this->position; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function setPosition(int $position): self |
|
|
|
public function setPosition(int $position): self |
|
|
|
{ |
|
|
|
{ |
|
|
|
$this->position = $position; |
|
|
|
$this->position = $position; |
|
|
|
|
|
|
|
|
|
|
|
return $this; |
|
|
|
return $this; |
|
|
|
} |
|
|
|
} |
|
|
|
/** |
|
|
|
|
|
|
|
* Get position. |
|
|
|
public function getTms(): DateTime |
|
|
|
* |
|
|
|
|
|
|
|
* @return int |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public function getPosition() |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
return $this->position; |
|
|
|
return $this->tms; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function setTms(DateTime $tms): self |
|
|
|
public function setTms(DateTime $tms): self |
|
|
|
{ |
|
|
|
{ |
|
|
|
$this->tms = $tms; |
|
|
|
$this->tms = $tms; |
|
|
|
|
|
|
|
|
|
|
|
return $this; |
|
|
|
return $this; |
|
|
|
} |
|
|
|
} |
|
|
|
/** |
|
|
|
|
|
|
|
* Get tms. |
|
|
|
public function getFilename(): ?string |
|
|
|
* |
|
|
|
|
|
|
|
* @return DateTime |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public function getTms() |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
return $this->tms; |
|
|
|
return $this->filename; |
|
|
|
} |
|
|
|
} |
|
|
|
/** |
|
|
|
|
|
|
|
* Set filename. |
|
|
|
public function setFilename(string $filename): static |
|
|
|
* |
|
|
|
|
|
|
|
* @return TrackEAttempt |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public function setFilename(string $filename) |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
$this->filename = $filename; |
|
|
|
$this->filename = $filename; |
|
|
|
|
|
|
|
|
|
|
|
return $this; |
|
|
|
return $this; |
|
|
|
} |
|
|
|
} |
|
|
|
/** |
|
|
|
|
|
|
|
* Get filename. |
|
|
|
public function getId(): ?int |
|
|
|
* |
|
|
|
|
|
|
|
* @return string |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public function getFilename() |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
return $this->filename; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* Get id. |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @return int |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
public function getId() |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
return $this->id; |
|
|
|
return $this->id; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function getUser(): User |
|
|
|
public function getUser(): User |
|
|
|
{ |
|
|
|
{ |
|
|
|
return $this->user; |
|
|
|
return $this->user; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function setUser(User $user): self |
|
|
|
public function setUser(User $user): self |
|
|
|
{ |
|
|
|
{ |
|
|
|
$this->user = $user; |
|
|
|
$this->user = $user; |
|
|
|
|
|
|
|
|
|
|
|
return $this; |
|
|
|
return $this; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function getTrackEExercise(): TrackEExercise |
|
|
|
public function getTrackEExercise(): TrackEExercise |
|
|
|
{ |
|
|
|
{ |
|
|
|
return $this->trackExercise; |
|
|
|
return $this->trackExercise; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function setTrackEExercise(TrackEExercise $trackExercise): self |
|
|
|
public function setTrackEExercise(TrackEExercise $trackExercise): self |
|
|
|
{ |
|
|
|
{ |
|
|
|
$this->trackExercise = $trackExercise; |
|
|
|
$this->trackExercise = $trackExercise; |
|
|
|
|
|
|
|
|
|
|
|
return $this; |
|
|
|
return $this; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function getSecondsSpent(): int |
|
|
|
public function getSecondsSpent(): int |
|
|
|
{ |
|
|
|
{ |
|
|
|
return $this->secondsSpent; |
|
|
|
return $this->secondsSpent; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function setSecondsSpent(int $secondsSpent): self |
|
|
|
public function setSecondsSpent(int $secondsSpent): self |
|
|
|
{ |
|
|
|
{ |
|
|
|
$this->secondsSpent = $secondsSpent; |
|
|
|
$this->secondsSpent = $secondsSpent; |
|
|
|
|
|
|
|
|
|
|
|
return $this; |
|
|
|
return $this; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* @return AttemptFile[]|Collection |
|
|
|
* @return Collection<int, AttemptFile> |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public function getAttemptFiles(): array|Collection |
|
|
|
public function getAttemptFiles(): Collection |
|
|
|
{ |
|
|
|
{ |
|
|
|
return $this->attemptFiles; |
|
|
|
return $this->attemptFiles; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* @param AttemptFile[]|Collection $attemptFiles |
|
|
|
* @param Collection<int, AttemptFile> $attemptFiles |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public function setAttemptFiles(array|Collection $attemptFiles): self |
|
|
|
public function setAttemptFiles(Collection $attemptFiles): self |
|
|
|
{ |
|
|
|
{ |
|
|
|
$this->attemptFiles = $attemptFiles; |
|
|
|
$this->attemptFiles = $attemptFiles; |
|
|
|
|
|
|
|
|
|
|
|
return $this; |
|
|
|
return $this; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* @return AttemptFeedback[]|Collection |
|
|
|
* @return Collection<int, AttemptFeedback> |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public function getAttemptFeedbacks(): array|Collection |
|
|
|
public function getAttemptFeedbacks(): Collection |
|
|
|
{ |
|
|
|
{ |
|
|
|
return $this->attemptFeedbacks; |
|
|
|
return $this->attemptFeedbacks; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* @param AttemptFeedback[]|Collection $attemptFeedbacks |
|
|
|
* @param Collection<int, AttemptFeedback> $attemptFeedbacks |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
public function setAttemptFeedbacks(array|Collection $attemptFeedbacks): self |
|
|
|
public function setAttemptFeedbacks(Collection $attemptFeedbacks): self |
|
|
|
{ |
|
|
|
{ |
|
|
|
$this->attemptFeedbacks = $attemptFeedbacks; |
|
|
|
$this->attemptFeedbacks = $attemptFeedbacks; |
|
|
|
|
|
|
|
|
|
|
|
return $this; |
|
|
|
return $this; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function addAttemptFeedback(AttemptFeedback $attemptFeedback): self |
|
|
|
public function addAttemptFeedback(AttemptFeedback $attemptFeedback): self |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (!$this->attemptFeedbacks->contains($attemptFeedback)) { |
|
|
|
if (!$this->attemptFeedbacks->contains($attemptFeedback)) { |
|
|
@ -274,6 +296,7 @@ class TrackEAttempt |
|
|
|
|
|
|
|
|
|
|
|
return $this; |
|
|
|
return $this; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public function addAttemptFile(AttemptFile $attemptFile): self |
|
|
|
public function addAttemptFile(AttemptFile $attemptFile): self |
|
|
|
{ |
|
|
|
{ |
|
|
|
if (!$this->attemptFiles->contains($attemptFile)) { |
|
|
|
if (!$this->attemptFiles->contains($attemptFile)) { |
|
|
|