Skip to content

Commit 4f2c40d

Browse files
authored
Database fixes (#27)
* Database fixes * Workflow saving to db * Run formatters * Run formatter * Some tests * Fix tests * Some util tests * Not working tests * More tests * More more tests * Fix non django tests * Add missing migration * Higher coverage * Run formatters * Run black * Bump version
1 parent f2b2dd8 commit 4f2c40d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+1912
-166
lines changed

example_workflows/string_regs.json

Lines changed: 629 additions & 0 deletions
Large diffs are not rendered by default.

src/sio3pack/__init__.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "1.0.0.dev2"
1+
__version__ = "1.0.0.dev3"
22

33
from sio3pack.files import LocalFile
44
from sio3pack.packages.exceptions import *
@@ -22,18 +22,21 @@ def from_file(file: str | LocalFile, configuration: SIO3PackConfig = None) -> Pa
2222
return Package.from_file(file, configuration=configuration)
2323

2424

25-
def from_db(problem_id: int) -> Package:
25+
def from_db(problem_id: int, configuration: SIO3PackConfig = None) -> Package:
2626
"""
2727
Initialize a package object from the database.
2828
If sio3pack isn't installed with Django support, it should raise an ImproperlyConfigured exception.
2929
If there is no package with the given problem_id, it should raise an UnknownPackageType exception.
3030
3131
:param problem_id: The problem id.
32+
:param configuration: Configuration of the package.
3233
:return: The package object.
3334
"""
3435
try:
35-
import django
36+
from django.conf import settings
3637

37-
return Package.from_db(problem_id)
38+
configuration = configuration or SIO3PackConfig()
39+
configuration.django_settings = settings
40+
return Package.from_db(problem_id, configuration)
3841
except ImportError:
3942
raise ImproperlyConfigured("sio3pack is not installed with Django support.")

src/sio3pack/django/common/handler.py

Lines changed: 85 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
1-
from typing import Any, Type
1+
import json
2+
from typing import Any
23

34
from django.core.files import File
45
from django.db import transaction
56

67
import sio3pack
7-
from sio3pack.django.common.models import SIO3Package, SIO3PackModelSolution, SIO3PackNameTranslation, SIO3PackStatement
8-
from sio3pack.files import LocalFile, RemoteFile
9-
from sio3pack.packages.exceptions import ImproperlyConfigured, PackageAlreadyExists
8+
from sio3pack.django.common.models import (
9+
SIO3Package,
10+
SIO3PackMainModelSolution,
11+
SIO3PackModelSolution,
12+
SIO3PackNameTranslation,
13+
SIO3PackStatement,
14+
SIO3PackTest,
15+
SIO3PackWorkflow,
16+
)
17+
from sio3pack.files import LocalFile
18+
from sio3pack.files.remote_file import RemoteFile
19+
from sio3pack.packages.exceptions import PackageAlreadyExists
20+
from sio3pack.test import Test
21+
from sio3pack.workflow import Workflow
1022

1123

1224
class DjangoHandler:
@@ -47,7 +59,9 @@ def save_to_db(self):
4759

4860
self._save_translated_titles()
4961
self._save_model_solutions()
62+
self._save_main_model_solution()
5063
self._save_problem_statements()
64+
self._save_tests()
5165

5266
def _save_translated_titles(self):
5367
"""
@@ -60,6 +74,18 @@ def _save_translated_titles(self):
6074
name=title,
6175
)
6276

77+
def _save_main_model_solution(self):
78+
"""
79+
Save the main model solution to the database.
80+
"""
81+
instance = SIO3PackMainModelSolution(
82+
package=self.db_package,
83+
)
84+
instance.source_file.save(
85+
self.package.main_model_solution.filename, File(open(self.package.main_model_solution.path, "rb"))
86+
)
87+
instance.save()
88+
6389
def _save_model_solutions(self):
6490
for order, solution in enumerate(self.package.model_solutions):
6591
instance = SIO3PackModelSolution(
@@ -68,6 +94,7 @@ def _save_model_solutions(self):
6894
order_key=order,
6995
)
7096
instance.source_file.save(solution.filename, File(open(solution.path, "rb")))
97+
instance.save()
7198

7299
def _save_problem_statements(self):
73100
def _add_statement(language: str, statement: LocalFile):
@@ -82,6 +109,28 @@ def _add_statement(language: str, statement: LocalFile):
82109
for lang, statement in self.package.lang_statements.items():
83110
_add_statement(lang, statement)
84111

112+
def _save_tests(self):
113+
for test in self.package.tests:
114+
instance = SIO3PackTest(
115+
package=self.db_package,
116+
name=test.test_name,
117+
test_id=test.test_id,
118+
group=test.group,
119+
)
120+
if test.in_file:
121+
instance.input_file.save(test.in_file.filename, File(open(test.in_file.path, "rb")))
122+
if test.out_file:
123+
instance.output_file.save(test.out_file.filename, File(open(test.out_file.path, "rb")))
124+
instance.save()
125+
126+
def _save_workflows(self):
127+
for name, wf in self.package.workflow_manager.all().items():
128+
instance = SIO3PackWorkflow(
129+
package=self.db_package,
130+
name=name,
131+
workflow_raw=json.dumps(wf.to_json()),
132+
)
133+
85134
@property
86135
def short_name(self) -> str:
87136
"""
@@ -110,11 +159,41 @@ def model_solutions(self) -> list[dict[str, Any]]:
110159
A list of model solutions, where each element is a dictionary containing
111160
a :class:`sio3pack.RemoteFile` object.
112161
"""
113-
return [{"file": RemoteFile(s.source_file.path)} for s in self.db_package.model_solutions.all()]
162+
return [{"file": RemoteFile(s.source_file)} for s in self.db_package.model_solutions.all()]
163+
164+
@property
165+
def main_model_solution(self) -> RemoteFile:
166+
"""
167+
The main model solution as a :class:`sio3pack.RemoteFile`.
168+
"""
169+
return RemoteFile(self.db_package.main_model_solution.source_file)
114170

115171
@property
116172
def lang_statements(self) -> dict[str, RemoteFile]:
117173
"""
118174
A dictionary of problem statements, where keys are language codes and values are files.
119175
"""
120-
return {s.language: RemoteFile(s.content.path) for s in self.db_package.statements.all()}
176+
return {s.language: RemoteFile(s.content) for s in self.db_package.statements.all()}
177+
178+
@property
179+
def tests(self) -> list[Test]:
180+
"""
181+
A list of tests, where each element is a dictionary containing
182+
"""
183+
return [
184+
Test(
185+
test_id=t.test_id,
186+
test_name=t.name,
187+
group=t.group,
188+
in_file=RemoteFile(t.input_file) if t.input_file else None,
189+
out_file=RemoteFile(t.output_file) if t.output_file else None,
190+
)
191+
for t in self.db_package.tests.all()
192+
]
193+
194+
@property
195+
def workflows(self) -> dict[str, Workflow]:
196+
"""
197+
A dictionary of workflows, where keys are workflow names and values are :class:`sio3pack.Workflow` objects.
198+
"""
199+
return {w.name: w.workflow for w in self.db_package.workflows.all()}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Generated by Django 4.2.20 on 2025-05-05 09:29
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
import sio3pack.django.common.models
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
("common", "0003_alter_sio3packnametranslation_language_and_more"),
12+
]
13+
14+
operations = [
15+
migrations.CreateModel(
16+
name="SIO3PackTest",
17+
fields=[
18+
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
19+
("name", models.CharField(max_length=255, verbose_name="name")),
20+
("test_id", models.CharField(max_length=255, verbose_name="id")),
21+
("group", models.CharField(max_length=255, verbose_name="group")),
22+
(
23+
"input_file",
24+
models.FileField(
25+
blank=True,
26+
null=True,
27+
upload_to=sio3pack.django.common.models.make_problem_filename,
28+
verbose_name="input file",
29+
),
30+
),
31+
(
32+
"output_file",
33+
models.FileField(
34+
blank=True,
35+
null=True,
36+
upload_to=sio3pack.django.common.models.make_problem_filename,
37+
verbose_name="output file",
38+
),
39+
),
40+
(
41+
"package",
42+
models.ForeignKey(
43+
on_delete=django.db.models.deletion.CASCADE, related_name="tests", to="common.sio3package"
44+
),
45+
),
46+
],
47+
),
48+
migrations.CreateModel(
49+
name="SIO3PackMainModelSolution",
50+
fields=[
51+
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
52+
(
53+
"source_file",
54+
models.FileField(
55+
upload_to=sio3pack.django.common.models.make_problem_filename, verbose_name="source file"
56+
),
57+
),
58+
(
59+
"package",
60+
models.ForeignKey(
61+
on_delete=django.db.models.deletion.CASCADE,
62+
related_name="main_model_solution",
63+
to="common.sio3package",
64+
),
65+
),
66+
],
67+
),
68+
]
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Generated by Django 4.2.20 on 2025-05-05 09:48
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
("common", "0004_sio3packtest_sio3packmainmodelsolution"),
11+
]
12+
13+
operations = [
14+
migrations.AlterModelOptions(
15+
name="sio3packtest",
16+
options={"verbose_name": "test", "verbose_name_plural": "tests"},
17+
),
18+
migrations.AlterUniqueTogether(
19+
name="sio3packtest",
20+
unique_together={("package", "name", "test_id", "group")},
21+
),
22+
migrations.CreateModel(
23+
name="SIO3PackWorkflow",
24+
fields=[
25+
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
26+
("name", models.CharField(max_length=255, verbose_name="name")),
27+
("workflow_raw", models.TextField(verbose_name="workflow")),
28+
(
29+
"package",
30+
models.ForeignKey(
31+
on_delete=django.db.models.deletion.CASCADE, related_name="workflows", to="common.sio3package"
32+
),
33+
),
34+
],
35+
options={
36+
"verbose_name": "workflow",
37+
"verbose_name_plural": "workflows",
38+
},
39+
),
40+
]
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Generated by Django 4.2.20 on 2025-05-08 13:31
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
("common", "0005_alter_sio3packtest_options_and_more"),
11+
]
12+
13+
operations = [
14+
migrations.AlterField(
15+
model_name="sio3packmainmodelsolution",
16+
name="package",
17+
field=models.OneToOneField(
18+
on_delete=django.db.models.deletion.CASCADE, related_name="main_model_solution", to="common.sio3package"
19+
),
20+
),
21+
migrations.AlterField(
22+
model_name="sio3packnametranslation",
23+
name="language",
24+
field=models.CharField(
25+
choices=[("en", "English"), ("pl", "Polski")], max_length=7, verbose_name="language code"
26+
),
27+
),
28+
migrations.AlterField(
29+
model_name="sio3packstatement",
30+
name="language",
31+
field=models.CharField(
32+
blank=True,
33+
choices=[("en", "English"), ("pl", "Polski")],
34+
max_length=7,
35+
null=True,
36+
verbose_name="language code",
37+
),
38+
),
39+
]

src/sio3pack/django/common/models.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
import json
12
import os
23

34
from django.conf import settings
45
from django.db import models
56
from django.utils.text import get_valid_filename
67
from django.utils.translation import gettext_lazy as _
78

9+
from sio3pack.workflow import Workflow
10+
811
try:
912
from oioioi.filetracker.fields import FileField
1013
except ImportError:
@@ -67,6 +70,18 @@ def short_name(self):
6770
return self.name.rsplit(".", 1)[0]
6871

6972

73+
class SIO3PackMainModelSolution(models.Model):
74+
package = models.OneToOneField(SIO3Package, on_delete=models.CASCADE, related_name="main_model_solution")
75+
source_file = FileField(upload_to=make_problem_filename, verbose_name=_("source file"))
76+
77+
def __str__(self):
78+
return f"<SIO3PackMainModelSolution {self.package.short_name}>"
79+
80+
@property
81+
def short_name(self):
82+
return self.source_file.name.rsplit(".", 1)[0]
83+
84+
7085
class SIO3PackStatement(models.Model):
7186
package = models.ForeignKey(SIO3Package, on_delete=models.CASCADE, related_name="statements")
7287
language = models.CharField(
@@ -92,3 +107,37 @@ def __str__(self):
92107
class Meta(object):
93108
verbose_name = _("problem statement")
94109
verbose_name_plural = _("problem statements")
110+
111+
112+
class SIO3PackTest(models.Model):
113+
package = models.ForeignKey(SIO3Package, on_delete=models.CASCADE, related_name="tests")
114+
name = models.CharField(max_length=255, verbose_name=_("name"))
115+
test_id = models.CharField(max_length=255, verbose_name=_("id"))
116+
group = models.CharField(max_length=255, verbose_name=_("group"))
117+
input_file = FileField(upload_to=make_problem_filename, verbose_name=_("input file"), blank=True, null=True)
118+
output_file = FileField(upload_to=make_problem_filename, verbose_name=_("output file"), blank=True, null=True)
119+
120+
def __str__(self):
121+
return f"<SIO3PackTest {self.name}>"
122+
123+
class Meta(object):
124+
verbose_name = _("test")
125+
verbose_name_plural = _("tests")
126+
unique_together = ("package", "name", "test_id", "group")
127+
128+
129+
class SIO3PackWorkflow(models.Model):
130+
package = models.ForeignKey(SIO3Package, on_delete=models.CASCADE, related_name="workflows")
131+
name = models.CharField(max_length=255, verbose_name=_("name"))
132+
workflow_raw = models.TextField(verbose_name=_("workflow"))
133+
134+
def __str__(self):
135+
return f"<SIO3PackWorkflow {self.name}>"
136+
137+
@property
138+
def workflow(self) -> Workflow:
139+
return Workflow.from_json(json.loads(self.workflow_raw))
140+
141+
class Meta(object):
142+
verbose_name = _("workflow")
143+
verbose_name_plural = _("workflows")

0 commit comments

Comments
 (0)