Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 36 additions & 6 deletions app/V1Module/presenters/PlagiarismPresenter.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use App\Model\Entity\PlagiarismDetectedSimilarity;
use App\Model\Entity\PlagiarismDetectedSimilarFile;
use App\Model\Entity\SolutionFile;
use App\Model\Repository\Assignments;
use App\Model\Repository\AssignmentSolutions;
use App\Model\Repository\PlagiarismDetectionBatches;
use App\Model\Repository\PlagiarismDetectedSimilarities;
Expand All @@ -32,6 +33,12 @@
*/
class PlagiarismPresenter extends BasePresenter
{
/**
* @var Assignments
* @inject
*/
public $assignments;

/**
* @var AssignmentSolutions
* @inject
Expand Down Expand Up @@ -168,15 +175,38 @@ public function checkUpdateBatch(string $id): void
* Update detection bath record. At the moment, only the uploadCompletedAt can be changed.
* @POST
*/
#[Post("uploadCompleted", new VBool(), "Whether the upload of the batch data is completed or not.")]
#[Post(
"uploadCompleted",
new VBool(),
"Whether the upload of the batch data is completed or not.",
required: false
)]
#[Post(
"assignments",
new VArray(new VUuid()),
"List of assignment IDs to be marked as 'checked' by this batch.",
required: false
)]
#[Path("id", new VUuid(), "Identification of the detection batch", required: true)]
public function actionUpdateBatch(string $id): void
{
$req = $this->getRequest();
$uploadCompleted = filter_var($req->getPost("uploadCompleted"), FILTER_VALIDATE_BOOLEAN);
$batch = $this->detectionBatches->findOrThrow($id);
$batch->setUploadCompleted($uploadCompleted);
$this->detectionBatches->persist($batch);
$req = $this->getRequest();

$uploadCompleted = $req->getPost("uploadCompleted");
if ($uploadCompleted !== null) {
$uploadCompleted = filter_var($uploadCompleted, FILTER_VALIDATE_BOOLEAN);
$batch->setUploadCompleted($uploadCompleted);
}

$assignments = $req->getPost("assignments") ?? [];
foreach ($assignments as $assignmentId) {
$assignment = $this->assignments->findOrThrow($assignmentId);
$assignment->setPlagiarismBatch($batch);
$this->assignments->persist($assignment, false); // no flush
}

$this->detectionBatches->persist($batch); // and flush
$this->sendSuccessResponse($batch);
}

Expand Down Expand Up @@ -281,7 +311,7 @@ public function actionAddSimilarities(string $id, string $solutionId): void
}

try {
$detectedFile = new PlagiarismDetectedSimilarFile(
new PlagiarismDetectedSimilarFile(
$detectedSimilarity,
$similarSolution,
$similarFile,
Expand Down
17 changes: 17 additions & 0 deletions app/model/entity/Assignment.php
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,13 @@ public function syncWithExercise()
$this->syncedAt = new DateTime();
}

/**
* @var PlagiarismDetectionBatch|null
* @ORM\ManyToOne(targetEntity="PlagiarismDetectionBatch")
* Refers to last plagiarism detection batch which checked solutions of this assignment.
*/
protected $plagiarismBatch = null;

/*
* Accessors
*/
Expand Down Expand Up @@ -702,4 +709,14 @@ public function setMaxPointsDeadlineInterpolation(bool $interpolation = true): v
{
$this->maxPointsDeadlineInterpolation = $interpolation;
}

public function getPlagiarismBatch(): ?PlagiarismDetectionBatch
{
return $this->plagiarismBatch;
}

public function setPlagiarismBatch(?PlagiarismDetectionBatch $batch = null)
{
$this->plagiarismBatch = $batch;
}
}
1 change: 1 addition & 0 deletions app/model/view/AssignmentViewFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ function (LocalizedExercise $text) use ($assignment) {
],
"solutionFilesLimit" => $assignment->getSolutionFilesLimit(),
"solutionSizeLimit" => $assignment->getSolutionSizeLimit(),
"plagiarismCheckedAt" => $assignment->getPlagiarismBatch()?->getUploadCompletedAt()?->getTimestamp(),
"permissionHints" => PermissionHints::get($this->assignmentAcl, $assignment)
];
}
Expand Down
35 changes: 35 additions & 0 deletions migrations/Version20250411155056.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

declare(strict_types=1);

namespace Migrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20250411155056 extends AbstractMigration
{
public function getDescription(): string
{
return '';
}

public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE assignment ADD plagiarism_batch_id CHAR(36) DEFAULT NULL COMMENT \'(DC2Type:uuid)\'');
$this->addSql('ALTER TABLE assignment ADD CONSTRAINT FK_30C544BA5B4CC7BF FOREIGN KEY (plagiarism_batch_id) REFERENCES plagiarism_detection_batch (id)');
$this->addSql('CREATE INDEX IDX_30C544BA5B4CC7BF ON assignment (plagiarism_batch_id)');
}

public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE assignment DROP FOREIGN KEY FK_30C544BA5B4CC7BF');
$this->addSql('DROP INDEX IDX_30C544BA5B4CC7BF ON assignment');
$this->addSql('ALTER TABLE assignment DROP plagiarism_batch_id');
}
}
62 changes: 54 additions & 8 deletions tests/Presenters/PlagiarismPresenter.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,52 @@ class TestPlagiarismPresenter extends Tester\TestCase
Assert::true($batch->getUploadCompletedAt() === null);
}

public function testBatchSetCompletedAndMarkAssignments()
{
PresenterTestHelper::loginDefaultAdmin($this->container);

$batch = current(array_filter($this->presenter->detectionBatches->findAll(), function ($b) {
return $b->getUploadCompletedAt() === null;
}));
Assert::notNull($batch);

$assignments = [];
$otherAssignments = [];
foreach ($this->presenter->assignments->findAll() as $assignment) {
if ($assignment->isExam()) {
$otherAssignments[] = $assignment;
} else {
$assignments[] = $assignment;
}
}
Assert::count(2, $assignments);
Assert::count(1, $otherAssignments);

$payload = PresenterTestHelper::performPresenterRequest(
$this->presenter,
'V1:PlagiarismPresenter',
'POST',
['action' => 'updateBatch', 'id' => $batch->getId()],
[
'assignments' => array_map(function ($a) {
return $a->getId();
}, $assignments)
]
);
Assert::equal($batch->getId(), $payload->getId());
Assert::true($payload->getUploadCompletedAt() === null);

foreach ($assignments as $assignment) {
$this->presenter->assignments->refresh($assignment);
Assert::equal($batch->getId(), $assignment->getPlagiarismBatch()?->getId());
}

foreach ($otherAssignments as $assignment) {
$this->presenter->assignments->refresh($assignment);
Assert::null($assignment->getPlagiarismBatch());
}
}

public function testGetSimilarities()
{
PresenterTestHelper::loginDefaultAdmin($this->container);
Expand Down Expand Up @@ -237,16 +283,16 @@ class TestPlagiarismPresenter extends Tester\TestCase
'fileEntry' => $similarity->getFileEntry(),
'fragments' => [
[
[ 'offset' => 42, 'length' => 54 ],
[ 'offset' => 42, 'length' => 54 ],
['offset' => 42, 'length' => 54],
['offset' => 42, 'length' => 54],
],
[
[ 'offset' => 420, 'length' => 540 ],
[ 'offset' => 420, 'length' => 540 ],
['offset' => 420, 'length' => 540],
['offset' => 420, 'length' => 540],
],
[
[ 'offset' => 4200, 'length' => 1024 ],
[ 'offset' => 4200, 'length' => 1024 ],
['offset' => 4200, 'length' => 1024],
['offset' => 4200, 'length' => 1024],
],
]
]],
Expand Down Expand Up @@ -476,8 +522,8 @@ class TestPlagiarismPresenter extends Tester\TestCase
'fileEntry' => $similarity->getFileEntry(),
'fragments' => [
[
[ 'off' => 42, 'length' => 54 ],
[ 'offset' => 42, 'len' => 54 ],
['off' => 42, 'length' => 54],
['offset' => 42, 'len' => 54],
],
]
]],
Expand Down