Skip to content

Commit

Permalink
Split submission file source code from submission files.
Browse files Browse the repository at this point in the history
Similar to DOMjudge#2241 this gets rid of the only other partial query.
  • Loading branch information
nickygerritsen committed Nov 24, 2023
1 parent e187de8 commit a93f189
Show file tree
Hide file tree
Showing 12 changed files with 156 additions and 27 deletions.
42 changes: 42 additions & 0 deletions webapp/migrations/Version20231124162457.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

declare(strict_types=1);

namespace DoctrineMigrations;

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

/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20231124162457 extends AbstractMigration
{
public function getDescription(): string
{
return 'Split submission file contents from submission files';
}

public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE submission_file_content (submitfileid INT UNSIGNED NOT NULL COMMENT \'Submission file ID\', sourcecode LONGBLOB NOT NULL COMMENT \'Submission file sourcecode(DC2Type:blobtext)\', PRIMARY KEY(submitfileid)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB COMMENT = \'Stores contents of submission files\' ');
$this->addSql('ALTER TABLE submission_file_content ADD CONSTRAINT FK_5846B4F7834A5B68 FOREIGN KEY (submitfileid) REFERENCES submission_file (submitfileid) ON DELETE CASCADE');
$this->addSql('INSERT INTO submission_file_content (submitfileid, sourcecode) SELECT submitfileid, sourcecode FROM submission_file');
$this->addSql('ALTER TABLE submission_file DROP sourcecode');
}

public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE submission_file ADD sourcecode LONGBLOB NOT NULL COMMENT \'Full source code(DC2Type:blobtext)\'');
$this->addSql('UPDATE submission_file INNER JOIN submission_file_content USING (submitfileid) SET submission_file.sourcecode = submission_file_content.sourcecode');
$this->addSql('ALTER TABLE submission_file_content DROP FOREIGN KEY FK_5846B4F7834A5B68');
$this->addSql('DROP TABLE submission_file_content');
}

public function isTransactional(): bool
{
return false;
}
}
5 changes: 3 additions & 2 deletions webapp/src/Controller/API/JudgehostController.php
Original file line number Diff line number Diff line change
Expand Up @@ -1335,7 +1335,8 @@ private function getSourceFiles(string $id): array
{
$queryBuilder = $this->em->createQueryBuilder()
->from(SubmissionFile::class, 'f')
->select('f')
->join('f.content', 'c')
->select('f', 'c')
->andWhere('f.submission = :submitid')
->setParameter('submitid', $id)
->orderBy('f.ranknumber');
Expand All @@ -1351,7 +1352,7 @@ private function getSourceFiles(string $id): array
foreach ($files as $file) {
$result[] = [
'filename' => $file->getFilename(),
'content' => base64_encode($file->getSourcecode()),
'content' => base64_encode($file->getContent()->getSourcecode()),
];
}
return $result;
Expand Down
5 changes: 3 additions & 2 deletions webapp/src/Controller/API/SubmissionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -514,7 +514,8 @@ public function getSubmissionSourceCodeAction(Request $request, string $id): arr
$queryBuilder = $this->em->createQueryBuilder()
->from(SubmissionFile::class, 'f')
->join('f.submission', 's')
->select('f, s')
->join('.f.content', 'c')
->select('f, s, c')
->andWhere('s.contest = :cid')
->andWhere('s.submitid = :submitid')
->setParameter('cid', $this->getContestId($request))
Expand All @@ -534,7 +535,7 @@ public function getSubmissionSourceCodeAction(Request $request, string $id): arr
'id' => (string)$file->getSubmitfileid(),
'submission_id' => (string)$file->getSubmission()->getSubmitid(),
'filename' => $file->getFilename(),
'source' => base64_encode($file->getSourcecode()),
'source' => base64_encode($file->getContent()->getSourcecode()),
];
}
return $result;
Expand Down
2 changes: 1 addition & 1 deletion webapp/src/Controller/Jury/ProblemController.php
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ public function exportAction(int $problemId): StreamedResponse
$directory = sprintf('submissions/%s/s%d/', $problemResult, $solution->getSubmitid());
/** @var SubmissionFile $source */
foreach ($solution->getFiles() as $source) {
$zip->addFromString($directory . $source->getFilename(), $source->getSourcecode());
$zip->addFromString($directory . $source->getFilename(), $source->getContent()->getSourcecode());
}
}
$zip->close();
Expand Down
29 changes: 19 additions & 10 deletions webapp/src/Controller/Jury/SubmissionController.php
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ public function viewAction(
->leftJoin('s.files', 'f')
->leftJoin('s.external_judgements', 'ej', Join::WITH, 'ej.valid = 1')
->leftJoin('s.contest_problem', 'cp')
->select('s', 't', 'p', 'l', 'c', 'partial f.{submitfileid, filename}', 'cp', 'ej')
->select('s', 't', 'p', 'l', 'c', 'f', 'cp', 'ej')
->andWhere('s.submitid = :submitid')
->setParameter('submitid', $submitId)
->getQuery()
Expand Down Expand Up @@ -697,7 +697,8 @@ public function sourceAction(
/** @var SubmissionFile|null $file */
$file = $this->em->createQueryBuilder()
->from(SubmissionFile::class, 'file')
->select('file')
->join('file.content', 'content')
->select('file, content')
->andWhere('file.ranknumber = :ranknumber')
->andWhere('file.submission = :submission')
->setParameter('ranknumber', $fetch)
Expand All @@ -713,16 +714,17 @@ public function sourceAction(
$response->headers->set('Content-Type',
sprintf('text/plain; name="%s"; charset="utf-8"', $file->getFilename()));
$response->headers->set('Content-Disposition', sprintf('attachment; filename="%s"', $file->getFilename()));
$response->headers->set('Content-Length', (string)strlen($file->getSourcecode()));
$response->setContent($file->getSourcecode());
$response->headers->set('Content-Length', (string)strlen($file->getContent()->getSourcecode()));
$response->setContent($file->getContent()->getSourcecode());

return $response;
}

/** @var SubmissionFile[] $files */
$files = $this->em->createQueryBuilder()
->from(SubmissionFile::class, 'file')
->select('file')
->join('file.content', 'content')
->select('file, content')
->andWhere('file.submission = :submission')
->setParameter('submission', $submission)
->orderBy('file.ranknumber')
Expand All @@ -738,7 +740,8 @@ public function sourceAction(
/** @var SubmissionFile[] $files */
$originalFiles = $this->em->createQueryBuilder()
->from(SubmissionFile::class, 'file')
->select('file')
->join('file.content', 'content')
->select('file, content')
->andWhere('file.submission = :submission')
->setParameter('submission', $originalSubmission)
->orderBy('file.ranknumber')
Expand Down Expand Up @@ -782,7 +785,8 @@ public function sourceAction(
/** @var SubmissionFile[] $files */
$oldFiles = $this->em->createQueryBuilder()
->from(SubmissionFile::class, 'file')
->select('file')
->join('file.content', 'content')
->select('file, content')
->andWhere('file.submission = :submission')
->setParameter('submission', $oldSubmission)
->orderBy('file.ranknumber')
Expand Down Expand Up @@ -818,7 +822,8 @@ public function editSourceAction(Request $request, Submission $submission, #[Map
/** @var SubmissionFile[] $files */
$files = $this->em->createQueryBuilder()
->from(SubmissionFile::class, 'file')
->select('file')
->join('file.content', 'content')
->select('file, content')
->andWhere('file.submission = :submission')
->setParameter('submission', $submission)
->orderBy('file.ranknumber')
Expand All @@ -832,7 +837,7 @@ public function editSourceAction(Request $request, Submission $submission, #[Map
];

foreach ($files as $file) {
$data['source' . $file->getRank()] = $file->getSourcecode();
$data['source' . $file->getRank()] = $file->getContent()->getSourcecode();
}

$formBuilder = $this->createFormBuilder($data)
Expand Down Expand Up @@ -1082,6 +1087,10 @@ public function verifyShadowDifferenceAction(
return $this->redirectToLocalReferrer($this->router, $request, $redirect);
}

/**
* @param SubmissionFile[] $files
* @param SubmissionFile[] $oldFiles
*/
protected function determineFileChanged(array $files, array $oldFiles): array
{
$result = [
Expand All @@ -1098,7 +1107,7 @@ protected function determineFileChanged(array $files, array $oldFiles): array
foreach ($files as $newfile) {
foreach ($oldFiles as $oldFile) {
if ($newfile->getFilename() === $oldFile->getFilename()) {
if ($oldFile->getSourcecode() === $newfile->getSourcecode()) {
if ($oldFile->getContent()->getSourcecode() === $newfile->getContent()->getSourcecode()) {
$result['unchanged'][] = $newfile->getFilename();
} else {
$result['changed'][] = $newfile->getFilename();
Expand Down
34 changes: 28 additions & 6 deletions webapp/src/Entity/SubmissionFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace App\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;

/**
Expand Down Expand Up @@ -36,8 +38,25 @@ class SubmissionFile
#[ORM\JoinColumn(name: 'submitid', referencedColumnName: 'submitid', onDelete: 'CASCADE')]
private Submission $submission;

#[ORM\Column(type: 'blobtext', options: ['comment' => 'Full source code'])]
private string $sourcecode;
/**
* @var Collection<int, SubmissionFileContent>
*
* We use a OneToMany instead of a OneToOne here, because otherwise this
* relation will always be loaded. See the commit message of commit
* 9e421f96691ec67ed62767fe465a6d8751edd884 for a more elaborate explanation
*/
#[ORM\OneToMany(
mappedBy: 'submissionFile',
targetEntity: SubmissionFileContent::class,
cascade: ['persist'],
orphanRemoval: true
)]
private Collection $content;

public function __construct()
{
$this->content = new ArrayCollection();
}

public function getSubmitfileid(): int
{
Expand Down Expand Up @@ -77,14 +96,17 @@ public function getSubmission(): Submission
return $this->submission;
}

public function setSourcecode(string $sourcecode): SubmissionFile
public function setContent(SubmissionFileContent $content): self
{
$this->sourcecode = $sourcecode;
$this->content->clear();
$this->content->add($content);
$content->setSubmissionFile($this);

return $this;
}

public function getSourcecode(): string
public function getContent(): ?SubmissionFileContent
{
return $this->sourcecode;
return $this->content->first() ?: null;
}
}
51 changes: 51 additions & 0 deletions webapp/src/Entity/SubmissionFileContent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php declare(strict_types=1);

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
#[ORM\Table(options: [
'collation' => 'utf8mb4_unicode_ci',
'charset' => 'utf8mb4',
'comment' => 'Stores contents of submission files',
])]
class SubmissionFileContent
{
/**
* We use a ManyToOne instead of a OneToOne here, because otherwise the
* reverse of this relation will always be loaded. See the commit message of commit
* 9e421f96691ec67ed62767fe465a6d8751edd884 for a more elaborate explanation.
*/
#[ORM\Id]
#[ORM\ManyToOne(inversedBy: 'content')]
#[ORM\JoinColumn(name: 'submitfileid', referencedColumnName: 'submitfileid', onDelete: 'CASCADE')]
private SubmissionFile $submissionFile;

#[ORM\Column(type: 'blobtext', options: ['comment' => 'Submission file sourcecode'])]
private string $sourcecode;

public function getSubmissionFile(): SubmissionFile
{
return $this->submissionFile;
}

public function setSubmissionFile(SubmissionFile $submissionFile): self
{
$this->submissionFile = $submissionFile;

return $this;
}

public function getSourcecode(): string
{
return $this->sourcecode;
}

public function setSourcecode(string $sourcecode): self
{
$this->sourcecode = $sourcecode;

return $this;
}
}
7 changes: 5 additions & 2 deletions webapp/src/Service/SubmissionService.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use App\Entity\Problem;
use App\Entity\Submission;
use App\Entity\SubmissionFile;
use App\Entity\SubmissionFileContent;
use App\Entity\Team;
use App\Entity\User;
use App\Utils\FreezeData;
Expand Down Expand Up @@ -610,11 +611,13 @@ public function submitSolution(
$this->em->persist($submission);

foreach ($files as $rank => $file) {
$submissionFileContent = (new SubmissionFileContent())
->setSourcecode(file_get_contents($file->getRealPath()));
$submissionFile = new SubmissionFile();
$submissionFile
->setFilename($file->getClientOriginalName())
->setRank($rank)
->setSourcecode(file_get_contents($file->getRealPath()));
->setContent($submissionFileContent);
$submissionFile->setSubmission($submission);
$this->em->persist($submissionFile);
}
Expand Down Expand Up @@ -767,7 +770,7 @@ public function getSubmissionZipResponse(Submission $submission): StreamedRespon
throw new ServiceUnavailableHttpException(null, "Could not create temporary zip file.");
}
foreach ($files as $file) {
$zip->addFromString($file->getFilename(), $file->getSourcecode());
$zip->addFromString($file->getFilename(), $file->getContent()->getSourcecode());
}
$zip->close();

Expand Down
2 changes: 1 addition & 1 deletion webapp/src/Twig/TwigExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -877,7 +877,7 @@ protected function parseSourceDiff(string $difftext): string
public function showDiff(SubmissionFile $newFile, SubmissionFile $oldFile): string
{
$differ = new Differ;
return $this->parseSourceDiff($differ->diff($oldFile->getSourcecode(), $newFile->getSourcecode()));
return $this->parseSourceDiff($differ->diff($oldFile->getContent()->getSourcecode(), $newFile->getContent()->getSourcecode()));
}

public function printContestStart(Contest $contest): string
Expand Down
2 changes: 1 addition & 1 deletion webapp/templates/jury/submission_edit_source.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

<div class="tab-pane fade {% if (selected is null and loop.first) or selected == file.rank %}show active{% endif %}"
id="source-{{ file.rank }}" role="tabpanel">
{{ file.sourcecode | codeEditor(idx, submission.language.aceLanguage, true, 'form_source' ~ file.rank) }}
{{ file.conte.tsourcecode | codeEditor(idx, submission.language.aceLanguage, true, 'form_source' ~ file.rank) }}
<script>
$(function () {
$('#form_source{{ file.rank }}').closest('div.mb-3').hide();
Expand Down
2 changes: 1 addition & 1 deletion webapp/templates/jury/submission_source.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
</a>
</div>

{{ file.sourcecode | codeEditor(file.rank, submission.language.aceLanguage) }}
{{ file.content.sourcecode | codeEditor(file.rank, submission.language.aceLanguage) }}
</div>
{%- endfor %}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ public function testAddSuccess(
$submissionFiles = [];
/** @var SubmissionFile $file */
foreach ($submission->getFiles() as $file) {
$submissionFiles[$file->getFilename()] = $file->getSourcecode();
$submissionFiles[$file->getFilename()] = $file->getContent()->getSourcecode();
}
static::assertEquals($expectedFiles, $submissionFiles, 'Wrong files');

Expand Down

0 comments on commit a93f189

Please sign in to comment.