checkpoint

This commit is contained in:
Egor Matveev
2021-09-05 15:28:24 +03:00
parent 1307c16ec1
commit 807c52bf2b
30 changed files with 394 additions and 26 deletions

View File

@@ -8,3 +8,4 @@ from Main.models.settask import SetTask
from Main.models.solution import Solution
from Main.models.language import Language
from Main.models.extrafile import ExtraFile
from Main.models.progress import Progress

View File

@@ -1,27 +1,50 @@
from os import remove
from os.path import join, exists
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from Sprint.settings import DATA_ROOT
class ExtraFile(models.Model):
task = models.ForeignKey('Task', on_delete=models.CASCADE)
task = models.ForeignKey("Task", on_delete=models.CASCADE)
filename = models.TextField()
is_test = models.BooleanField(null=True)
is_sample = models.BooleanField(null=True)
readable = models.BooleanField(null=True)
test_number = models.IntegerField(null=True)
@property
def path(self):
return join(DATA_ROOT, 'extra_files', str(self.id))
return join(DATA_ROOT, "extra_files", str(self.id))
@property
def can_be_sample(self):
return (
self.is_test
and not self.filename.endswith(".a")
and len(
ExtraFile.objects.filter(task=self.task, filename=self.filename + ".a")
)
)
@property
def text(self):
return open(self.path, 'r').read()
return open(self.path, "r").read()
def delete(self, using=None, keep_parents=False):
if exists(self.path):
remove(self.path)
if self.is_test and self.filename.endswith('.a'):
try:
ef = ExtraFile.objects.get(task=self.task, filename=self.filename.rstrip('.a'), is_test=True)
ef.is_sample = False
ef.save()
except ObjectDoesNotExist:
pass
super().delete(using=using, keep_parents=keep_parents)
@property
def answer(self):
return ExtraFile.objects.get(task=self.task, is_test=True, filename=self.filename + '.a')

View File

@@ -7,6 +7,7 @@ class Language(models.Model):
file_type = models.TextField(null=True)
logo = models.ImageField(upload_to="logos", null=True)
image = models.TextField(default='ubuntu')
highlight = models.TextField(default='plaintext')
opened = models.BooleanField(default=False)
def __str__(self):

35
Main/models/progress.py Normal file
View File

@@ -0,0 +1,35 @@
from datetime import timedelta
from django.contrib.auth.models import User
from django.db import models
from django.utils import timezone
from Main.models import Task
class Progress(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
task = models.ForeignKey(Task, on_delete=models.CASCADE)
start_time = models.DateTimeField(default=timezone.now)
finished_time = models.DateTimeField(null=True)
score = models.IntegerField(default=0)
finished = models.BooleanField(default=False)
@property
def time(self):
if not self.finished:
self.finished_time = timezone.now()
return self.finished_time - self.start_time
def increment_rating(self):
if self.task.creator == self.user:
return
delta = timedelta(minutes=self.task.time_estimation)
self.score = int(delta / self.time * 100)
self.save()
self.user.userinfo.rating += self.score
self.user.userinfo.save()
@staticmethod
def by_solution(solution):
return Progress.objects.get(task=solution.task, user=solution.user)

View File

@@ -1,8 +1,20 @@
from django.contrib.auth.models import User
from django.db import models
from django.utils import timezone
class Set(models.Model):
name = models.TextField()
public = models.BooleanField(default=False)
creator = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
opened = models.BooleanField(default=False)
start_time = models.DateTimeField(default=timezone.now)
end_time = models.DateTimeField(default=timezone.now)
@property
def available(self):
return (
self.opened
and (self.start_time is None or timezone.now() >= self.start_time)
and (self.end_time is None or timezone.now() <= self.end_time)
)

View File

@@ -1,9 +1,10 @@
from os import mkdir
from os import mkdir, walk
from os.path import join, exists
from shutil import rmtree
from subprocess import call
from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.utils import timezone
@@ -24,6 +25,30 @@ class Solution(models.Model):
rmtree(self.directory)
super().delete(using=using, keep_parents=keep_parents)
@property
def files(self):
data = []
for path, _, files in walk(self.directory):
if path.startswith(self.testing_directory):
continue
for file in files:
try:
entity = {
'filename': file,
'text': open(join(path, file), 'r').read()
}
end = file.split('.')[-1]
try:
highlight = 'language-' + Language.objects.get(file_type=end).highlight
except ObjectDoesNotExist:
highlight = 'nohighlight'
entity['highlight'] = highlight
data.append(entity)
except:
continue
data.sort(key=lambda x: x['filename'])
return data
def create_dirs(self):
mkdir(self.directory)
mkdir(self.testing_directory)

View File

@@ -12,6 +12,7 @@ class Task(models.Model):
output_format = models.TextField(default="")
specifications = models.TextField(default="")
time_limit = models.IntegerField(default=10000)
time_estimation = models.IntegerField(default=5)
creator = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
def __str__(self):
@@ -24,3 +25,25 @@ class Task(models.Model):
@property
def tests(self):
return ExtraFile.objects.filter(task=self, is_test=True)
@property
def samples(self):
data = []
for test in self.tests.order_by('test_number'):
if test.is_sample and test.readable:
data.append({
'input': test.text,
'output': test.answer.text
})
count = 1
for entity in data:
entity["num"] = count
count += 1
return data
def delete(self, using=None, keep_parents=False):
from Main.models.progress import Progress
for progress in Progress.objects.filter(task=self):
progress.user.userinfo.rating -= progress.score
progress.user.userinfo.save()
super().delete(using=using, keep_parents=keep_parents)

View File

@@ -17,6 +17,8 @@ class UserInfo(models.Model):
profile_picture = models.ImageField(upload_to="profile_pictures", null=True)
rating = models.IntegerField(default=0)
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True)
telegram_chat_id = models.TextField(default="")
notification_solution_result = models.BooleanField(default=False)
def _append_task(self, task, tasks):
if task.creator == self.user or task.public:

View File

@@ -38,3 +38,11 @@ class AccountView(BaseView):
self.request.user.save()
login(self.request, self.request.user)
return "/account?error_message=Пароль успешно установлен"
def post_notifications(self):
self.request.user.userinfo.telegram_chat_id = self.request.POST['chat_id']
for attr in dir(self.request.user.userinfo):
if attr.startswith('notification'):
setattr(self.request.user.userinfo, attr, attr in self.request.POST.keys())
self.request.user.userinfo.save()
return '/account'

View File

@@ -12,8 +12,7 @@ class RegisterView(BaseView):
self.context["error_message"] = self.request.GET.get("error_message", "")
def post(self):
data = {**self.request.POST}
data["password"] = data["password"].strip()
data = self.request.POST
if len(data["password"]) < 8:
return "/register?error_message=Пароль слишком слабый"
if data["password"] != data["repeat_password"]:

View File

@@ -0,0 +1,10 @@
from SprintLib.BaseView import BaseView, AccessError
class SolutionView(BaseView):
view_file = 'solution.html'
required_login = True
def pre_handle(self):
if self.entities.solution.user != self.request.user:
raise AccessError()

View File

@@ -0,0 +1,17 @@
from django.http import HttpResponse
from Main.models import Progress
from SprintLib.BaseView import BaseView
class TaskRuntimeView(BaseView):
view_file = 'task_runtime.html'
required_login = True
def get(self):
progress = Progress.objects.get(task=self.entities.task, user=self.request.user)
self.context['progress'] = progress
if 'render' in self.request.GET.keys():
return
if progress.finished:
return HttpResponse('done')

View File

@@ -1,7 +1,7 @@
from django.core.exceptions import ObjectDoesNotExist
from django.http import HttpResponse
from django.utils import timezone
from Main.models import ExtraFile
from Main.models import ExtraFile, Progress
from SprintLib.BaseView import BaseView, AccessError
@@ -12,6 +12,10 @@ class TaskSettingsView(BaseView):
def pre_handle(self):
if self.entities.task not in self.request.user.userinfo.available_tasks:
raise AccessError()
if self.request.method == 'POST':
for progress in Progress.objects.filter(task=self.entities.task, finished=False):
progress.start_time = timezone.now()
progress.save()
def get(self):
self.context['error_message'] = self.request.GET.get('error_message', '')
@@ -29,8 +33,10 @@ class TaskSettingsView(BaseView):
name = filename.strip('.a')
if not name.isnumeric():
return f'/admin/task?task_id={self.entities.task.id}&error_message=Формат файла не соответствует тесту'
ef, created = ExtraFile.objects.get_or_create(task=self.entities.task, is_test=True, test_number=int(name))
ef, created = ExtraFile.objects.get_or_create(task=self.entities.task, is_test=True, filename=filename)
if not created:
ef.is_sample = False
ef.save()
return f'/admin/task?task_id={self.entities.task.id}'
if ef is None or created is None:
ef, created = ExtraFile.objects.get_or_create(
@@ -78,3 +84,11 @@ class TaskSettingsView(BaseView):
def post_create_test(self):
return self._create(True)
def post_save_test(self):
ef = ExtraFile.objects.get(id=self.request.POST['test_id'])
with open(ef.path, 'w') as fs:
fs.write(self.request.POST['text'])
ef.is_sample = 'is_sample' in self.request.POST.keys()
ef.save()
return f'/admin/task?task_id={self.entities.task.id}'

View File

@@ -1,6 +1,6 @@
from zipfile import ZipFile
from Main.models import Solution
from Main.models import Solution, Progress
from Main.tasks import start_testing
from SprintLib.BaseView import BaseView, Language
from SprintLib.testers import *
@@ -12,6 +12,8 @@ class TaskView(BaseView):
def get(self):
self.context['languages'] = Language.objects.filter(opened=True).order_by('name')
progress, _ = Progress.objects.get_or_create(user=self.request.user, task=self.entities.task)
self.context['progress'] = progress
def pre_handle(self):
if self.request.method == 'GET':

View File

@@ -1,6 +1,5 @@
from Main.models import Task
from SprintLib.BaseView import BaseView
from django.db.models import Q
class TasksView(BaseView):

View File

@@ -9,3 +9,5 @@ from Main.views.RatingView import RatingView
from Main.views.SetsView import SetsView
from Main.views.TaskView import TaskView
from Main.views.SolutionsTableView import SolutionsTableView
from Main.views.TaskRuntimeView import TaskRuntimeView
from Main.views.SolutionView import SolutionView