apply languages
This commit is contained in:
parent
1e455346ff
commit
2948c8252e
0
Checker/__init__.py
Normal file
0
Checker/__init__.py
Normal file
3
Checker/admin.py
Normal file
3
Checker/admin.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
6
Checker/apps.py
Normal file
6
Checker/apps.py
Normal file
@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class CheckerConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'Checker'
|
27
Checker/migrations/0001_initial.py
Normal file
27
Checker/migrations/0001_initial.py
Normal file
@ -0,0 +1,27 @@
|
||||
# Generated by Django 3.2.4 on 2022-02-15 18:22
|
||||
|
||||
import Checker.models
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('Main', '0020_languageapply'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Checker',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('token', models.CharField(db_index=True, default=Checker.models.generate_token, max_length=30)),
|
||||
('last_request', models.DateTimeField()),
|
||||
('set', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='Main.set')),
|
||||
('testing_solution', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='Main.solution')),
|
||||
],
|
||||
),
|
||||
]
|
20
Checker/migrations/0002_alter_checker_set.py
Normal file
20
Checker/migrations/0002_alter_checker_set.py
Normal file
@ -0,0 +1,20 @@
|
||||
# Generated by Django 3.2.4 on 2022-02-15 18:23
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('Main', '0020_languageapply'),
|
||||
('Checker', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='checker',
|
||||
name='set',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='checkers', to='Main.set'),
|
||||
),
|
||||
]
|
18
Checker/migrations/0003_checker_name.py
Normal file
18
Checker/migrations/0003_checker_name.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 3.2.4 on 2022-02-15 18:24
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('Checker', '0002_alter_checker_set'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='checker',
|
||||
name='name',
|
||||
field=models.CharField(default='', max_length=50),
|
||||
),
|
||||
]
|
19
Checker/migrations/0004_alter_checker_token.py
Normal file
19
Checker/migrations/0004_alter_checker_token.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Generated by Django 3.2.4 on 2022-02-15 18:48
|
||||
|
||||
import Checker.models
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('Checker', '0003_checker_name'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='checker',
|
||||
name='token',
|
||||
field=models.CharField(db_index=True, default=Checker.models.generate_token, max_length=30, unique=True),
|
||||
),
|
||||
]
|
19
Checker/migrations/0005_checker_dynamic_token.py
Normal file
19
Checker/migrations/0005_checker_dynamic_token.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Generated by Django 3.2.4 on 2022-02-16 07:33
|
||||
|
||||
import Checker.models
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('Checker', '0004_alter_checker_token'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='checker',
|
||||
name='dynamic_token',
|
||||
field=models.CharField(db_index=True, default=Checker.models.generate_token, max_length=30, unique=True),
|
||||
),
|
||||
]
|
0
Checker/migrations/__init__.py
Normal file
0
Checker/migrations/__init__.py
Normal file
28
Checker/models.py
Normal file
28
Checker/models.py
Normal file
@ -0,0 +1,28 @@
|
||||
from random import choice
|
||||
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
|
||||
from Main.models import Solution, Set
|
||||
|
||||
|
||||
def generate_token():
|
||||
letters = '1234567890qwertyuiopasdfghjklzxcvbnm!@#$%^&*()QWERTYUIOPASDFGHJKLZXCVBNM'
|
||||
return ''.join([choice(letters) for _ in range(30)])
|
||||
|
||||
|
||||
class Checker(models.Model):
|
||||
token = models.CharField(max_length=30, default=generate_token, db_index=True, unique=True)
|
||||
dynamic_token = models.CharField(max_length=30, default=generate_token, db_index=True, unique=True)
|
||||
testing_solution = models.ForeignKey(Solution, on_delete=models.SET_NULL, null=True)
|
||||
set = models.ForeignKey(Set, on_delete=models.CASCADE, related_name="checkers")
|
||||
last_request = models.DateTimeField()
|
||||
name = models.CharField(max_length=50, default='')
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
if self.testing_solution is not None:
|
||||
return 'Testing'
|
||||
if (timezone.now() - self.last_request).total_seconds() > 3:
|
||||
return 'Disabled'
|
||||
return 'Active'
|
3
Checker/tests.py
Normal file
3
Checker/tests.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
10
Checker/urls.py
Normal file
10
Checker/urls.py
Normal file
@ -0,0 +1,10 @@
|
||||
from django.urls import path
|
||||
|
||||
from Checker import views
|
||||
|
||||
urlpatterns = [
|
||||
path("status", views.status),
|
||||
path("available", views.available),
|
||||
path("get_dynamic", views.get_dynamic),
|
||||
path("set_result", views.set_result),
|
||||
]
|
73
Checker/views.py
Normal file
73
Checker/views.py
Normal file
@ -0,0 +1,73 @@
|
||||
from os.path import join
|
||||
from tempfile import TemporaryDirectory
|
||||
from zipfile import ZipFile
|
||||
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.http import JsonResponse, HttpResponse
|
||||
|
||||
from django.utils import timezone
|
||||
|
||||
from Checker.models import Checker, generate_token
|
||||
from FileStorage.sync import synchronized_method
|
||||
from Main.models import Solution, SolutionFile, ExtraFile
|
||||
|
||||
|
||||
def get_dynamic(request):
|
||||
try:
|
||||
checker = Checker.objects.get(token=request.GET['token'])
|
||||
if checker.status == 'Active':
|
||||
return JsonResponse({"status": "Another checker is working"}, status=403)
|
||||
checker.dynamic_token = generate_token()
|
||||
checker.save()
|
||||
return JsonResponse({"token": checker.dynamic_token})
|
||||
except ObjectDoesNotExist:
|
||||
return JsonResponse({"status": "incorrect token"}, status=403)
|
||||
|
||||
|
||||
def status(request):
|
||||
try:
|
||||
checker = Checker.objects.get(dynamic_token=request.GET['token'])
|
||||
now = timezone.now()
|
||||
checker.last_request = now
|
||||
checker.save()
|
||||
return JsonResponse({"status": "ok"})
|
||||
except ObjectDoesNotExist:
|
||||
return JsonResponse({"status": "incorrect token"}, status=403)
|
||||
|
||||
|
||||
@synchronized_method
|
||||
def available(request):
|
||||
try:
|
||||
checker = Checker.objects.get(dynamic_token=request.GET['token'])
|
||||
solution = Solution.objects.filter(set=checker.set, result="In queue").order_by('time_sent').first()
|
||||
if solution is None:
|
||||
return JsonResponse({"id": None})
|
||||
solution.result = "Testing"
|
||||
solution.save()
|
||||
with TemporaryDirectory() as tempdir:
|
||||
with ZipFile(join(tempdir, "package.zip"), 'w') as zip_file:
|
||||
for sf in SolutionFile.objects.filter(solution=solution):
|
||||
zip_file.writestr(sf.path, sf.bytes)
|
||||
for ef in ExtraFile.objects.filter(task=solution.task):
|
||||
zip_file.writestr(ef.filename, ef.bytes)
|
||||
response = HttpResponse(open(join(tempdir, 'package.zip'), 'rb').read(), content_type='application/octet-stream', status=201)
|
||||
response.headers['language_id'] = solution.language_id
|
||||
response.headers['solution_id'] = solution.id
|
||||
response.headers['timeout'] = solution.task.time_limit
|
||||
return response
|
||||
except ObjectDoesNotExist:
|
||||
return JsonResponse({"status": "incorrect token"}, status=403)
|
||||
|
||||
|
||||
def set_result(request):
|
||||
try:
|
||||
checker = Checker.objects.get(dynamic_token=request.GET['token'])
|
||||
solution = Solution.objects.get(id=request.GET['solution_id'])
|
||||
result = request.GET['result']
|
||||
if checker.set != solution.set:
|
||||
return JsonResponse({"status": "incorrect solution"}, status=403)
|
||||
solution.result = result
|
||||
solution.save()
|
||||
return JsonResponse({"status": True})
|
||||
except ObjectDoesNotExist:
|
||||
return JsonResponse({"status": "incorrect token"}, status=403)
|
15
CheckerExecutor/Dockerfile
Normal file
15
CheckerExecutor/Dockerfile
Normal file
@ -0,0 +1,15 @@
|
||||
FROM docker:dind
|
||||
|
||||
RUN apk add --update --no-cache python3 && ln -sf python3 /usr/bin/python
|
||||
RUN python3 -m ensurepip
|
||||
RUN apk update && apk add postgresql-dev gcc python3-dev musl-dev jpeg-dev zlib-dev libjpeg
|
||||
RUN pip3 install --no-cache --upgrade pip setuptools
|
||||
RUN addgroup -S docker
|
||||
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
RUN mkdir -p /usr/src/app/
|
||||
WORKDIR /usr/src/app/
|
||||
|
||||
COPY . /usr/src/app/
|
||||
|
||||
CMD ["python", "main.py"]
|
0
CheckerExecutor/__init__.py
Normal file
0
CheckerExecutor/__init__.py
Normal file
64
CheckerExecutor/language.py
Normal file
64
CheckerExecutor/language.py
Normal file
@ -0,0 +1,64 @@
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass
|
||||
class Language:
|
||||
id: int
|
||||
name: str
|
||||
work_name: str
|
||||
file_type: str
|
||||
logo_url: str
|
||||
image: str
|
||||
highlight: str
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
languages = [
|
||||
Language(
|
||||
id=0,
|
||||
name="Python3",
|
||||
work_name="Python3",
|
||||
file_type="py",
|
||||
logo_url="https://entredatos.es/wp-content/uploads/2021/05/1200px-Python-logo-notext.svg.png",
|
||||
image="python:3.6",
|
||||
highlight="python",
|
||||
),
|
||||
Language(
|
||||
id=1,
|
||||
name="Kotlin",
|
||||
work_name="Kotlin",
|
||||
file_type="kt",
|
||||
logo_url="https://upload.wikimedia.org/wikipedia/commons/0/06/Kotlin_Icon.svg",
|
||||
image="zenika/kotlin",
|
||||
highlight="kotlin",
|
||||
),
|
||||
Language(
|
||||
id=2,
|
||||
name="C++",
|
||||
work_name="Cpp",
|
||||
file_type="cpp",
|
||||
logo_url="https://upload.wikimedia.org/wikipedia/commons/thumb/1/18/ISO_C%2B%2B_Logo.svg/1822px-ISO_C%2B%2B_Logo.svg.png",
|
||||
image="gcc",
|
||||
highlight="cpp",
|
||||
),
|
||||
Language(
|
||||
id=3,
|
||||
name="Java",
|
||||
work_name="Java",
|
||||
file_type="java",
|
||||
logo_url="https://upload.wikimedia.org/wikipedia/ru/thumb/3/39/Java_logo.svg/1200px-Java_logo.svg.png",
|
||||
image="openjdk",
|
||||
highlight="java",
|
||||
),
|
||||
Language(
|
||||
id=4,
|
||||
name="C#",
|
||||
work_name="CSharp",
|
||||
file_type="cs",
|
||||
logo_url="https://cdn.worldvectorlogo.com/logos/c--4.svg",
|
||||
image="mono",
|
||||
highlight="csharp",
|
||||
),
|
||||
]
|
67
CheckerExecutor/main.py
Normal file
67
CheckerExecutor/main.py
Normal file
@ -0,0 +1,67 @@
|
||||
from multiprocessing import Process
|
||||
from os import getenv
|
||||
from os.path import join
|
||||
from tempfile import TemporaryDirectory
|
||||
from time import sleep
|
||||
from zipfile import ZipFile
|
||||
|
||||
from requests import get
|
||||
|
||||
from language import languages
|
||||
from testers import *
|
||||
|
||||
|
||||
def process_solution(path, data, language_id, solution_id, timeout):
|
||||
with open(join(path, "package.zip"), 'wb') as fs:
|
||||
fs.write(data)
|
||||
with ZipFile(join(path, "package.zip"), 'r') as zip_ref:
|
||||
zip_ref.extractall(path)
|
||||
language = languages[language_id]
|
||||
try:
|
||||
result = eval(language.work_name + "Tester")(path, solution_id, language_id, timeout).execute()
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
result = "TE"
|
||||
return result
|
||||
|
||||
|
||||
def poll(token):
|
||||
while True:
|
||||
print(get("http://127.0.0.1:8000/checker/status", params={"token": token}).json())
|
||||
sleep(2)
|
||||
|
||||
|
||||
def main():
|
||||
request = get("http://127.0.0.1:8000/checker/get_dynamic", params={"token": getenv("TOKEN")})
|
||||
if request.status_code != 200:
|
||||
print("Error happened: " + request.json()['status'])
|
||||
exit(1)
|
||||
dynamic_token = request.json()['token']
|
||||
p = Process(target=poll, args=(dynamic_token,))
|
||||
p.start()
|
||||
while True:
|
||||
data = get("http://127.0.0.1:8000/checker/available", params={"token": dynamic_token})
|
||||
if data.status_code == 200:
|
||||
sleep(2)
|
||||
continue
|
||||
elif data.status_code == 201:
|
||||
with TemporaryDirectory() as tempdir:
|
||||
result = process_solution(
|
||||
tempdir,
|
||||
data.content,
|
||||
int(data.headers['language_id']),
|
||||
int(data.headers['solution_id']),
|
||||
int(data.headers['timeout']),
|
||||
)
|
||||
get("http://127.0.0.1:8000/checker/set_result", params={
|
||||
"token": dynamic_token,
|
||||
"solution_id": data.headers['solution_id'],
|
||||
"result": result
|
||||
})
|
||||
|
||||
else:
|
||||
print("unknown status")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
115
CheckerExecutor/testers/BaseTester.py
Normal file
115
CheckerExecutor/testers/BaseTester.py
Normal file
@ -0,0 +1,115 @@
|
||||
from os import listdir
|
||||
from os.path import join, exists
|
||||
from subprocess import call, TimeoutExpired
|
||||
|
||||
from language import *
|
||||
|
||||
|
||||
class TestException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class BaseTester:
|
||||
working_directory = "app"
|
||||
checker_code = None
|
||||
|
||||
def exec_command(self, command, working_directory="app", timeout=None):
|
||||
return call(
|
||||
f'docker exec -i solution sh -c "cd {working_directory} && {command}"',
|
||||
shell=True,
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
def before_test(self):
|
||||
files = [
|
||||
file
|
||||
for file in listdir(self.path)
|
||||
if file.endswith("." + self.language.file_type)
|
||||
]
|
||||
code = self.exec_command(
|
||||
f'{self.build_command} {" ".join(files)}',
|
||||
working_directory=self.working_directory,
|
||||
)
|
||||
if code != 0:
|
||||
raise TestException("CE")
|
||||
|
||||
def test(self, filename):
|
||||
code = self.exec_command(
|
||||
f"< {filename} {self.command} > output.txt",
|
||||
timeout=self.timeout / 1000,
|
||||
)
|
||||
if code != 0:
|
||||
raise TestException("RE")
|
||||
result = open(join(self.path, "output.txt"), "r").read().strip().replace('\r\n', '\n')
|
||||
print("got result", result)
|
||||
if self.checker_code is not None:
|
||||
print('using checker')
|
||||
with open(join(self.path, 'expected.txt'), 'w') as fs:
|
||||
fs.write(self.predicted)
|
||||
with open(join(self.path, 'checker.py'), 'w') as fs:
|
||||
fs.write(self.checker_code)
|
||||
code = call(f'docker exec -i checker sh -c "cd app && python checker.py"', shell=True, timeout=1)
|
||||
if code != 0:
|
||||
raise TestException("WA")
|
||||
else:
|
||||
print('using simple check')
|
||||
if result != self.predicted:
|
||||
print('incorrect')
|
||||
raise TestException("WA")
|
||||
print('correct')
|
||||
|
||||
def after_test(self):
|
||||
pass
|
||||
|
||||
@property
|
||||
def command(self):
|
||||
return "./executable.exe"
|
||||
|
||||
@property
|
||||
def build_command(self):
|
||||
return ""
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
return self._path
|
||||
|
||||
@property
|
||||
def language(self):
|
||||
return languages[self.language_id]
|
||||
|
||||
def __init__(self, path, solution_id, language_id, timeout):
|
||||
self.solution_id = solution_id
|
||||
self._path = path
|
||||
self.language_id = language_id
|
||||
self.timeout = timeout
|
||||
|
||||
def execute(self):
|
||||
docker_command = f"docker run --name solution --volume={self.path}:/{self.working_directory} -t -d {self.language.image}"
|
||||
print(docker_command)
|
||||
call(docker_command, shell=True)
|
||||
checker = join(self.path, 'checker.py')
|
||||
if exists(checker):
|
||||
self.checker_code = open(checker, 'r').read()
|
||||
call(f"docker run --name checker --volume={self.path}:/app -t -d python:3.6", shell=True)
|
||||
print("Container created")
|
||||
result = None
|
||||
try:
|
||||
self.before_test()
|
||||
print("before test finished")
|
||||
for file in listdir(self.path):
|
||||
if not file.endswith(".a") and exists(join(self.path, file + '.a')):
|
||||
self.predicted = open(join(self.path, file + '.a'), 'r').read().strip().replace('\r\n', '\n')
|
||||
print('predicted:', self.predicted)
|
||||
self.test(file)
|
||||
self.after_test()
|
||||
result = "OK"
|
||||
except TestException as e:
|
||||
result = str(e)
|
||||
except TimeoutExpired:
|
||||
result = "TL"
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
result = "TE"
|
||||
call(f"docker rm --force solution", shell=True)
|
||||
call(f"docker rm --force checker", shell=True)
|
||||
return result
|
11
CheckerExecutor/testers/CSharpTester.py
Normal file
11
CheckerExecutor/testers/CSharpTester.py
Normal file
@ -0,0 +1,11 @@
|
||||
from .BaseTester import BaseTester
|
||||
|
||||
|
||||
class CSharpTester(BaseTester):
|
||||
@property
|
||||
def build_command(self):
|
||||
return "csc /out:executable.exe"
|
||||
|
||||
@property
|
||||
def command(self):
|
||||
return "mono executable.exe"
|
7
CheckerExecutor/testers/CppTester.py
Normal file
7
CheckerExecutor/testers/CppTester.py
Normal file
@ -0,0 +1,7 @@
|
||||
from .BaseTester import BaseTester
|
||||
|
||||
|
||||
class CppTester(BaseTester):
|
||||
@property
|
||||
def build_command(self):
|
||||
return "g++ -o executable.exe"
|
8
CheckerExecutor/testers/GoTester.py
Normal file
8
CheckerExecutor/testers/GoTester.py
Normal file
@ -0,0 +1,8 @@
|
||||
from .BaseTester import BaseTester
|
||||
|
||||
|
||||
class GoTester(BaseTester):
|
||||
working_directory = "../app"
|
||||
|
||||
def build_command(self):
|
||||
return "go build -o executable.exe"
|
27
CheckerExecutor/testers/JavaTester.py
Normal file
27
CheckerExecutor/testers/JavaTester.py
Normal file
@ -0,0 +1,27 @@
|
||||
from os import listdir
|
||||
|
||||
from .BaseTester import BaseTester, TestException
|
||||
|
||||
|
||||
class JavaTester(BaseTester):
|
||||
_executable = None
|
||||
|
||||
def before_test(self):
|
||||
files = [
|
||||
file
|
||||
for file in listdir(self.path)
|
||||
if file.endswith(".java")
|
||||
]
|
||||
code = self.exec_command(f"javac {' '.join(files)}")
|
||||
if code != 0:
|
||||
raise TestException("CE")
|
||||
for file in listdir(self.path):
|
||||
if file.endswith(".class"):
|
||||
self._executable = file.rstrip(".class")
|
||||
break
|
||||
if self._executable is None:
|
||||
raise TestException("TE")
|
||||
|
||||
@property
|
||||
def command(self):
|
||||
return f"java -classpath . {self._executable}"
|
21
CheckerExecutor/testers/KotlinTester.py
Normal file
21
CheckerExecutor/testers/KotlinTester.py
Normal file
@ -0,0 +1,21 @@
|
||||
from os import listdir
|
||||
|
||||
from .BaseTester import BaseTester, TestException
|
||||
|
||||
|
||||
class KotlinTester(BaseTester):
|
||||
def before_test(self):
|
||||
files = [
|
||||
file
|
||||
for file in listdir(self.path)
|
||||
if file.endswith(".kt")
|
||||
]
|
||||
code = self.exec_command(
|
||||
f'kotlinc {" ".join(files)} -include-runtime -d solution.jar'
|
||||
)
|
||||
if code != 0:
|
||||
raise TestException("CE")
|
||||
|
||||
@property
|
||||
def command(self):
|
||||
return "java -jar solution.jar"
|
19
CheckerExecutor/testers/Python3Tester.py
Normal file
19
CheckerExecutor/testers/Python3Tester.py
Normal file
@ -0,0 +1,19 @@
|
||||
from os import listdir
|
||||
|
||||
from .BaseTester import BaseTester, TestException
|
||||
|
||||
|
||||
class Python3Tester(BaseTester):
|
||||
file = None
|
||||
|
||||
def before_test(self):
|
||||
for file in listdir(self.path):
|
||||
if file.endswith(".py") and file != 'checker.py':
|
||||
self.file = file
|
||||
break
|
||||
if self.file is None:
|
||||
raise TestException("TE")
|
||||
|
||||
@property
|
||||
def command(self):
|
||||
return f"python3 {self.file}"
|
7
CheckerExecutor/testers/__init__.py
Normal file
7
CheckerExecutor/testers/__init__.py
Normal file
@ -0,0 +1,7 @@
|
||||
from .BaseTester import BaseTester
|
||||
from .Python3Tester import Python3Tester
|
||||
from .CppTester import CppTester
|
||||
from .GoTester import GoTester
|
||||
from .JavaTester import JavaTester
|
||||
from .CSharpTester import CSharpTester
|
||||
from .KotlinTester import KotlinTester
|
23
Main/migrations/0021_auto_20220215_2211.py
Normal file
23
Main/migrations/0021_auto_20220215_2211.py
Normal file
@ -0,0 +1,23 @@
|
||||
# Generated by Django 3.2.4 on 2022-02-15 19:11
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('Main', '0020_languageapply'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='solution',
|
||||
name='set',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='Main.set'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='solution',
|
||||
index=models.Index(fields=['set', '-time_sent'], name='Main_soluti_set_id_19e6c7_idx'),
|
||||
),
|
||||
]
|
@ -4,9 +4,14 @@ from SprintLib.utils import get_bytes, write_bytes, delete_file
|
||||
|
||||
|
||||
class FileStorageMixin:
|
||||
|
||||
@cached_property
|
||||
def bytes(self):
|
||||
return get_bytes(self.fs_id)
|
||||
|
||||
@cached_property
|
||||
def text(self):
|
||||
return get_bytes(self.fs_id).decode("utf-8")
|
||||
return self.bytes.decode("utf-8")
|
||||
|
||||
def write(self, bytes):
|
||||
self.fs_id = write_bytes(bytes)
|
||||
|
@ -7,6 +7,7 @@ from django.contrib.auth.models import User
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
|
||||
from Main.models import Set
|
||||
from Main.models.solution_file import SolutionFile
|
||||
from Main.models.task import Task
|
||||
from Sprint.settings import CONSTS
|
||||
@ -20,11 +21,13 @@ class Solution(models.Model):
|
||||
time_sent = models.DateTimeField(default=timezone.now)
|
||||
result = models.TextField(default=CONSTS["in_queue_status"])
|
||||
test = models.IntegerField(default=None, null=True)
|
||||
set = models.ForeignKey(Set, null=True, on_delete=models.SET_NULL)
|
||||
|
||||
class Meta:
|
||||
indexes = [
|
||||
models.Index(fields=['task', 'user', '-time_sent']),
|
||||
models.Index(fields=['task', '-time_sent'])
|
||||
models.Index(fields=['task', '-time_sent']),
|
||||
models.Index(fields=['set', '-time_sent']),
|
||||
]
|
||||
|
||||
@property
|
||||
|
@ -1,6 +1,6 @@
|
||||
from django import template
|
||||
|
||||
from Main.models import Solution
|
||||
from Main.models import Solution, SetTask
|
||||
|
||||
register = template.Library()
|
||||
|
||||
@ -13,3 +13,8 @@ def solved(user, task):
|
||||
if len(solutions) != 0:
|
||||
return False
|
||||
return None
|
||||
|
||||
|
||||
@register.filter('settask')
|
||||
def settask(set, task):
|
||||
return SetTask.objects.get(set=set, task=task)
|
||||
|
24
Main/views/CheckersView.py
Normal file
24
Main/views/CheckersView.py
Normal file
@ -0,0 +1,24 @@
|
||||
import datetime
|
||||
from typing import Optional
|
||||
|
||||
import pytz
|
||||
from django.utils import timezone
|
||||
|
||||
from Checker.models import Checker
|
||||
from Main.models import SetTask, Set
|
||||
from SprintLib.BaseView import BaseView, AccessError
|
||||
from SprintLib.language import languages
|
||||
|
||||
|
||||
class CheckersView(BaseView):
|
||||
required_login = True
|
||||
view_file = "checkers.html"
|
||||
endpoint = "admin/checkers"
|
||||
|
||||
def pre_handle(self):
|
||||
self.current_set = self.entities.set
|
||||
if (
|
||||
self.request.user != self.current_set.creator
|
||||
and self.request.user.username not in self.current_set.editors
|
||||
):
|
||||
raise AccessError()
|
@ -4,6 +4,7 @@ from typing import Optional
|
||||
import pytz
|
||||
from django.utils import timezone
|
||||
|
||||
from Checker.models import Checker
|
||||
from Main.models import SetTask, Set
|
||||
from SprintLib.BaseView import BaseView, AccessError
|
||||
from SprintLib.language import languages
|
||||
@ -114,3 +115,11 @@ class SetSettingsView(BaseView):
|
||||
self.entities.set.languages.remove(t)
|
||||
self.entities.set.save()
|
||||
return "/admin/set?set_id=" + str(self.entities.set.id)
|
||||
|
||||
def post_new_checker(self):
|
||||
Checker.objects.create(name=self.request.POST['name'], set=self.entities.set, last_request=timezone.now() - datetime.timedelta(days=1))
|
||||
return '/admin/set?set_id=' + str(self.entities.set.id)
|
||||
|
||||
def post_delete_checker(self):
|
||||
Checker.objects.get(id=self.request.POST['checker_id']).delete()
|
||||
return '/admin/set?set_id=' + str(self.entities.set.id)
|
||||
|
@ -37,6 +37,7 @@ class TaskView(BaseView):
|
||||
task=self.entities.task,
|
||||
user=self.request.user,
|
||||
language_id=int(self.request.POST["language"]),
|
||||
set=self.entities.set if hasattr(self.entities, 'setTask') else None
|
||||
)
|
||||
|
||||
def post_0(self):
|
||||
@ -47,8 +48,8 @@ class TaskView(BaseView):
|
||||
solution=self.solution,
|
||||
fs_id=fs_id,
|
||||
)
|
||||
send_testing(self.solution.id)
|
||||
return "task?task_id=" + str(self.entities.task.id)
|
||||
send_testing(self.solution)
|
||||
return ("/task?setTask_id=" + str(self.entities.setTask.id)) if hasattr(self.entities, 'setTask') else ("/task?task_id=" + str(self.entities.task.id))
|
||||
|
||||
def post_1(self):
|
||||
# отправка решения через файл
|
||||
@ -73,5 +74,5 @@ class TaskView(BaseView):
|
||||
solution=self.solution,
|
||||
fs_id=fs_id,
|
||||
)
|
||||
send_testing(self.solution.id)
|
||||
return "task?task_id=" + str(self.entities.task.id)
|
||||
send_testing(self.solution)
|
||||
return ("/task?setTask_id=" + str(self.entities.setTask.id)) if hasattr(self.entities, 'setTask') else ("/task?task_id=" + str(self.entities.task.id))
|
||||
|
@ -20,3 +20,4 @@ from Main.views.ChatWithView import ChatWithView
|
||||
from Main.views.MessagesView import MessagesView
|
||||
from Main.views.SetView import SetView
|
||||
from Main.views.GroupView import GroupView
|
||||
from Main.views.CheckersView import CheckersView
|
||||
|
@ -43,6 +43,7 @@ INSTALLED_APPS = [
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
"Main.apps.MainConfig",
|
||||
"Checker.apps.CheckerConfig",
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
|
@ -1,10 +1,13 @@
|
||||
from django.contrib import admin
|
||||
from django.urls import path
|
||||
from django.urls import path, include
|
||||
|
||||
import Main.views
|
||||
from Main.views import *
|
||||
|
||||
urlpatterns = []
|
||||
|
||||
urlpatterns = [
|
||||
path("checker/", include("Checker.urls"))
|
||||
]
|
||||
|
||||
for v in dir(Main.views):
|
||||
try:
|
||||
|
@ -3,7 +3,9 @@ import pika
|
||||
from Sprint import settings
|
||||
|
||||
|
||||
def send_testing(solution_id):
|
||||
def send_testing(solution):
|
||||
if solution.set is not None and len(solution.set.checkers.all()) != 0:
|
||||
return
|
||||
with pika.BlockingConnection(
|
||||
pika.ConnectionParameters(host=settings.RABBIT_HOST, port=settings.RABBIT_PORT)
|
||||
) as connection:
|
||||
@ -12,5 +14,5 @@ def send_testing(solution_id):
|
||||
channel.basic_publish(
|
||||
exchange="",
|
||||
routing_key="test",
|
||||
body=bytes(str(solution_id), encoding="utf-8"),
|
||||
body=bytes(str(solution.id), encoding="utf-8"),
|
||||
)
|
||||
|
34
templates/checkers.html
Normal file
34
templates/checkers.html
Normal file
@ -0,0 +1,34 @@
|
||||
{% for checker in set.checkers.all %}
|
||||
{% with status=checker.status %}
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="delete_checker">
|
||||
<input type="hidden" name="checker_id" value="{{ checker.id }}">
|
||||
<button type="button" class="btn btn-link" data-toggle="modal" data-target="#exampleCheckers{{ checker.id }}">{{ checker.name }}</button> <span class="badge badge-{% if status == 'Active' %}success{% else %}{% if status == 'Testing' %}info{% else %}danger{% endif %}{% endif %}">{{ status }}</span>{% if status != 'Testing' %}<button type="submit" class="btn btn-link" style="color: black;"><i class="fa fa-times"></i></button>{% endif %}<br>
|
||||
</form>
|
||||
{% endwith %}
|
||||
<div class="modal fade" id="exampleCheckers{{ checker.id }}" tabindex="-1" role="dialog" aria-labelledby="exampleModalLongTitleCheckers{{ checker.id }}" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="exampleModalLongTitleCheckers{{ checker.id }}">Токен {{ checker.name }}</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
{{ checker.token }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-danger" data-dismiss="modal"><i class="fa fa-times-circle"></i> Закрыть</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
@ -3,12 +3,25 @@
|
||||
{% block title %}{{ set.name }}{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
var saved_data = "";
|
||||
function handle(value) {
|
||||
const elem = document.getElementById(value);
|
||||
elem.hidden = !elem.hidden;
|
||||
}
|
||||
function doPoll() {
|
||||
jQuery.get('/admin/checkers?set_id={{ set.id }}', function(data) {
|
||||
var e = document.getElementById('checkers');
|
||||
if (saved_data.length != data.length) {
|
||||
saved_data = data;
|
||||
e.innerHTML = data;
|
||||
}
|
||||
setTimeout(function() {doPoll()}, 2000);
|
||||
})
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
{% block onload %}doPoll(){% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
@ -160,4 +173,37 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<hr><hr>
|
||||
<h3>Чекеры</h3>
|
||||
<div id="checkers"></div>
|
||||
<button class="btn btn-light" data-toggle="modal" data-target="#exampleCheckers"><i class="fa fa-plus"></i> Добавить чекер</button>
|
||||
<div class="modal fade" id="exampleCheckers" tabindex="-1" role="dialog" aria-labelledby="exampleModalLongTitleCheckers" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<form method="POST">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="exampleModalLongTitleCheckers">Создать новый чекер</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="new_checker">
|
||||
<input type="text" name="name" placeholder="Имя чекера">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-danger" data-dismiss="modal"><i class="fa fa-times-circle"></i> Закрыть</button>
|
||||
<button type="submit" class="btn btn-success"><i class="fa fa-plus-circle"></i> Создать</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
@ -1,5 +1,7 @@
|
||||
{% extends 'base_main.html' %}
|
||||
|
||||
{% load filters %}
|
||||
|
||||
{% block main %}
|
||||
<h4>
|
||||
<table class="table" style="width: 30%;">
|
||||
@ -16,7 +18,7 @@
|
||||
Задача
|
||||
</td>
|
||||
<td>
|
||||
<a href="/task?task_id={{ solution.task.id }}">{{ solution.task.name }}</a>
|
||||
<a href="/task?{% if solution.set %}{% with settask=solution.set|settask:solution.task %}setTask_id={{ settask.id }}{% endwith %}{% else %}task_id={{ solution.task.id }}{% endif %}">{{ solution.task.name }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
Loading…
Reference in New Issue
Block a user