checkpoint
This commit is contained in:
parent
1307c16ec1
commit
807c52bf2b
@ -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
|
||||
|
@ -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')
|
@ -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
35
Main/models/progress.py
Normal 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)
|
@ -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)
|
||||
)
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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'
|
||||
|
@ -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"]:
|
||||
|
10
Main/views/SolutionView.py
Normal file
10
Main/views/SolutionView.py
Normal 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()
|
17
Main/views/TaskRuntimeView.py
Normal file
17
Main/views/TaskRuntimeView.py
Normal 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')
|
@ -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}'
|
||||
|
@ -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':
|
||||
|
@ -1,6 +1,5 @@
|
||||
from Main.models import Task
|
||||
from SprintLib.BaseView import BaseView
|
||||
from django.db.models import Q
|
||||
|
||||
|
||||
class TasksView(BaseView):
|
||||
|
@ -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
|
||||
|
@ -14,7 +14,9 @@ urlpatterns = [
|
||||
path("admin/task", TaskSettingsView.as_view()),
|
||||
path("sets", SetsView.as_view()),
|
||||
path("task", TaskView.as_view()),
|
||||
path("solution", SolutionView.as_view()),
|
||||
path("solutions_table", SolutionsTableView.as_view()),
|
||||
path("task_runtime", TaskRuntimeView.as_view()),
|
||||
path("", MainView.as_view()),
|
||||
path("admin/", admin.site.urls),
|
||||
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
|
@ -1,6 +1,5 @@
|
||||
import asyncio
|
||||
import sys
|
||||
from time import sleep
|
||||
|
||||
|
||||
class BaseDaemon:
|
||||
|
@ -4,8 +4,10 @@ from shutil import copyfile, rmtree
|
||||
from subprocess import call, TimeoutExpired
|
||||
|
||||
from Main.models import ExtraFile
|
||||
from Main.models.progress import Progress
|
||||
from Sprint.settings import CONSTS
|
||||
from SprintLib.utils import copy_content
|
||||
from bot import bot
|
||||
|
||||
|
||||
class TestException(Exception):
|
||||
@ -68,6 +70,12 @@ class BaseTester:
|
||||
self.test(test.filename)
|
||||
self.after_test()
|
||||
self.solution.result = CONSTS["ok_status"]
|
||||
progress = Progress.objects.get(user=self.solution.user, task=self.solution.task)
|
||||
if progress.finished_time is None:
|
||||
progress.finished_time = self.solution.time_sent
|
||||
progress.finished = True
|
||||
progress.save()
|
||||
progress.increment_rating()
|
||||
except TestException as e:
|
||||
self.solution.result = str(e)
|
||||
except TimeoutExpired:
|
||||
@ -78,3 +86,11 @@ class BaseTester:
|
||||
self.solution.save()
|
||||
call(f"docker rm --force solution_{self.solution.id}", shell=True)
|
||||
rmtree(self.solution.testing_directory)
|
||||
self.solution.user.userinfo.refresh_from_db()
|
||||
if self.solution.user.userinfo.notification_solution_result:
|
||||
bot.send_message(self.solution.user.userinfo.telegram_chat_id,
|
||||
f'Задача: {self.solution.task.name}\n'
|
||||
f'Результат: {self.solution.result}\n'
|
||||
f'Очки решения: {Progress.by_solution(self.solution).score}\n'
|
||||
f'Текущий рейтинг: {self.solution.user.userinfo.rating}',
|
||||
parse_mode='html')
|
||||
|
16
bot.py
Normal file
16
bot.py
Normal file
@ -0,0 +1,16 @@
|
||||
import telebot
|
||||
|
||||
|
||||
bot = telebot.TeleBot("1994460106:AAGrGsCZjF6DVG_T-zycELuVfxnWw8x7UyU")
|
||||
|
||||
|
||||
@bot.message_handler(commands=["start"])
|
||||
@bot.message_handler(content_types=["text"])
|
||||
def do_action(message):
|
||||
bot.send_message(chat_id=message.chat.id, text=f"ID чата: {message.chat.id}")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print('bot is starting')
|
||||
bot.polling()
|
||||
print('bot failed')
|
6
daemons/bot.py
Normal file
6
daemons/bot.py
Normal file
@ -0,0 +1,6 @@
|
||||
from SprintLib.BaseDaemon import BaseDaemon
|
||||
|
||||
|
||||
class Daemon(BaseDaemon):
|
||||
def command(self):
|
||||
return "python bot.py"
|
@ -3,4 +3,4 @@ from SprintLib.BaseDaemon import BaseDaemon
|
||||
|
||||
class Daemon(BaseDaemon):
|
||||
def command(self):
|
||||
return "python manage.py runserver"
|
||||
return "python manage.py runserver 0.0.0.0:80"
|
||||
|
@ -87,4 +87,25 @@
|
||||
<button type="submit" class="btn btn-light">Сменить пароль</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% if owner %}
|
||||
<hr><hr>
|
||||
<h2>Уведомления</h2>
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="action" value="notifications">
|
||||
<input type="text" name="chat_id" value="{{ user.userinfo.telegram_chat_id }}" placeholder="telegram chat id"> <a class="btn btn-link" target="_blank" rel="noopener noreferrer" href="https://t.me/sprint_notifications_bot">Бот</a>
|
||||
<table>
|
||||
<tr>
|
||||
<td style="width: 200px;">
|
||||
Результаты решений
|
||||
</td>
|
||||
<td>
|
||||
<input type="checkbox" name="notification_solution_result" {% if user.userinfo.notification_solution_result %}checked{% endif %}>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<button type="submit" class="btn btn-light" style="margin-top: 15px;"><i class="fa fa-save"></i> Сохранить</button>
|
||||
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endblock %}
|
@ -24,6 +24,10 @@
|
||||
<script type="text/javascript" id="MathJax-script" async
|
||||
src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js">
|
||||
</script>
|
||||
<link rel="stylesheet"
|
||||
href="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.2.0/build/styles/default.min.css">
|
||||
<script src="//cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.2.0/build/highlight.min.js"></script>
|
||||
<script>hljs.highlightAll();</script>
|
||||
<script type="text/javascript" src={% static "js/scripts.js" %}></script>
|
||||
<style type="text/css">
|
||||
.center {
|
||||
@ -65,6 +69,8 @@
|
||||
}
|
||||
.button-right {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
right:10%;
|
||||
}
|
||||
.task-settings-input {
|
||||
width: 50%;
|
||||
@ -74,9 +80,10 @@
|
||||
resize: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 700px){
|
||||
@media screen and (max-width: 1200px){
|
||||
.header-button {
|
||||
width:50px;
|
||||
width:140px;
|
||||
font-size: 15px;
|
||||
}
|
||||
}
|
||||
{% block styles %}{% endblock %}
|
||||
|
@ -19,7 +19,7 @@
|
||||
onclick="window.location.href='/rating'">
|
||||
<i class="fa fa-arrow-up"></i> Рейтинг
|
||||
</button>
|
||||
<div class="button-right" style="margin-top: -35px;">
|
||||
<div class="button-right">
|
||||
<button class="btn btn-light header-button"
|
||||
onclick="window.location.href='/account'">
|
||||
<i class="fa fa-user"></i> Аккаунт
|
||||
|
46
templates/solution.html
Normal file
46
templates/solution.html
Normal file
@ -0,0 +1,46 @@
|
||||
{% extends 'base_main.html' %}
|
||||
|
||||
{% block main %}
|
||||
<h4>
|
||||
<table class="table" style="width: 30%;">
|
||||
<tr>
|
||||
<td>
|
||||
Id решения
|
||||
</td>
|
||||
<td>
|
||||
{{ solution.id }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Задача
|
||||
</td>
|
||||
<td>
|
||||
<a href="/task?task_id={{ solution.task.id }}">{{ solution.task.name }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Язык
|
||||
</td>
|
||||
<td>
|
||||
<img src="{{ solution.language.logo.url }}" width="30px" height="30px">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Результат
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge badge-{% if solution.result == in_queue_status %}secondary{% else %}{% if solution.result == ok_status %}success{% else %}{% if solution.result == testing_status %}info{% else %}danger{% endif %}{% endif %}{% endif %}">{% if solution.result == testing_status %}<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="circle-notch" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="width: 20px;" class="svg-inline--fa fa-circle-notch fa-w-16 fa-spin fa-lg"><path fill="currentColor" d="M288 39.056v16.659c0 10.804 7.281 20.159 17.686 23.066C383.204 100.434 440 171.518 440 256c0 101.689-82.295 184-184 184-101.689 0-184-82.295-184-184 0-84.47 56.786-155.564 134.312-177.219C216.719 75.874 224 66.517 224 55.712V39.064c0-15.709-14.834-27.153-30.046-23.234C86.603 43.482 7.394 141.206 8.003 257.332c.72 137.052 111.477 246.956 248.531 246.667C393.255 503.711 504 392.788 504 256c0-115.633-79.14-212.779-186.211-240.236C302.678 11.889 288 23.456 288 39.056z" class=""></path></svg> {% endif %}{{ solution.result }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</h4>
|
||||
<h4>Файлы решения</h4>
|
||||
{% for entity in solution.files %}
|
||||
<h5>{{ entity.filename }}</h5>
|
||||
<pre><code class="{{ entity.highlight }}" style="border: 1px solid black;">{{ entity.text }}</code></pre>
|
||||
<hr>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
@ -1,7 +1,7 @@
|
||||
{% for solution in solutions %}
|
||||
<tr>
|
||||
<td>
|
||||
<b><a href="">{{ solution.id }}</a></b>
|
||||
<b><a href="/solution?solution_id={{ solution.id }}">{{ solution.id }}</a></b>
|
||||
</td>
|
||||
<td>
|
||||
{{ solution.time_sent }}
|
||||
|
@ -19,24 +19,31 @@
|
||||
}
|
||||
function doPoll() {
|
||||
jQuery.get('/solutions_table?task_id={{ task.id }}', function(data) {
|
||||
if (data == 'done') {
|
||||
return
|
||||
}
|
||||
else {
|
||||
document.getElementById('solutions').innerHTML = data;
|
||||
jQuery.get('/task_runtime?task_id={{ task.id }}', function(data1) {
|
||||
if (data == 'done' && data1 == 'done')
|
||||
return
|
||||
if (data != 'done') {
|
||||
document.getElementById('solutions').innerHTML = data;
|
||||
}
|
||||
if (data1 != 'done') {
|
||||
document.getElementById('runtime').innerHTML = data1;
|
||||
}
|
||||
setTimeout(function() {doPoll()}, 2000);
|
||||
}
|
||||
})
|
||||
})
|
||||
jQuery.get('/solutions_table?task_id={{ task.id }}&render=true', function(data) {
|
||||
jQuery.get('/solutions_table?id={{ task.id }}&render=true', function(data) {
|
||||
document.getElementById('solutions').innerHTML = data;
|
||||
})
|
||||
jQuery.get('/task_runtime?id={{ task.id }}&render=true', function(data) {
|
||||
document.getElementById('runtime').innerHTML = data;
|
||||
})
|
||||
}
|
||||
{% endblock %}
|
||||
|
||||
{% block onload %}doPoll(){% endblock %}
|
||||
|
||||
{% block main %}
|
||||
<h2>{{ task.name }}</h2>
|
||||
<div id="runtime"></div>
|
||||
{% if task.legend %}
|
||||
<h4>Легенда</h4>
|
||||
{% autoescape off %}
|
||||
@ -64,6 +71,40 @@
|
||||
{{ task.specifications }}
|
||||
{% endautoescape %}
|
||||
<hr>
|
||||
{% endif %}
|
||||
{% if task.samples %}
|
||||
<h4 style="">Примеры</h4>
|
||||
{% for sample in task.samples %}
|
||||
<h5>Пример {{ sample.num }}</h5>
|
||||
<b>
|
||||
<table style="width: 100%">
|
||||
<tr>
|
||||
<td>
|
||||
Входные данные
|
||||
</td>
|
||||
<td>
|
||||
Выходные данные
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</b>
|
||||
<hr>
|
||||
<table style="width: 100%;">
|
||||
<tr>
|
||||
<td style="width: 50%; vertical-align: top;">
|
||||
<pre>
|
||||
{{ sample.input }}
|
||||
</pre>
|
||||
</td>
|
||||
<td style="width: 50%; vertical-align: top;">
|
||||
<pre>
|
||||
{{ sample.output }}
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<hr>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<h2>Отправить решение</h2>
|
||||
<table style="margin-bottom: 10px;">
|
||||
|
1
templates/task_runtime.html
Normal file
1
templates/task_runtime.html
Normal file
@ -0,0 +1 @@
|
||||
<h2>{{ task.name }}<span style="margin-left: 15px;" class="badge badge-{% if progress.finished %}success{% else %}danger{% endif %}">{{ progress.time }}</span></h2>
|
@ -74,6 +74,14 @@
|
||||
<input type="text" name="time_limit" value="{{ task.time_limit }}" class="task-settings-input">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
Оценка времени решения (мин)
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="time_estimation" value="{{ task.time_estimation }}" class="task-settings-input">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<button type="submit" class="btn btn-light" style="margin-top: 15px;"><i class="fa fa-save"></i> Сохранить</button>
|
||||
</form>
|
||||
@ -93,8 +101,40 @@
|
||||
<td>
|
||||
{% for test in task.tests %}
|
||||
<div id="file_{{ test.id }}">
|
||||
<i class="fa fa-file"></i> <button class="btn btn-link" {% if not test.readable %}style="color: red;"{% endif %}>{{ test.filename }}</button><button class="btn btn-link" style="color: black;" onclick="deleteFile({{ test.id }});"><i class="fa fa-times"></i> </button><br>
|
||||
<i class="fa fa-file"></i> <button class="btn btn-link" {% if not test.readable %}style="color: red;" {% else %}data-toggle="modal" data-target="#filesModalLong{{ test.id }}"{% endif %}>{{ test.filename }}</button><button class="btn btn-link" style="color: black;" onclick="deleteFile({{ test.id }});"><i class="fa fa-times"></i> </button><br>
|
||||
{% if test.readable %}
|
||||
<form method="POST">{% csrf_token %}
|
||||
<!-- Modal -->
|
||||
<div class="modal fade bd-example-modal-lg" id="filesModalLong{{ test.id }}" tabindex="-1" role="dialog" aria-labelledby="filesModalLongTitle{{ test.id }}" aria-hidden="true">
|
||||
<div class="modal-dialog modal-lg" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="filesModalLongTitle{{ test.id }}">{{ test.filename }}</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">
|
||||
<textarea cols="82" rows="30" name="text">{{ test.text }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
{% if test.can_be_sample %}Использовать как пример <input type="checkbox" name="is_sample" {% if test.is_sample %}checked{% endif %}>{% endif %}
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal"><i class="fa fa-times"></i> Close</button>
|
||||
<input name="action" value="save_test" type="hidden">
|
||||
<input name="test_id" value="{{ test.id }}" type="hidden">
|
||||
<button type="submit" class="btn btn-primary"><i class="fa fa-save"></i> Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<input type="file" style="display: none;" form="form_test_upload" onchange="this.form.submit();" class="btn form-control-file" id="test-upload" value="Выбрать файл" name="file">
|
||||
<label for="test-upload" class="btn btn-primary"><i class="fa fa-upload"></i> Загрузить тесты</label><button style="margin-left: 10px; margin-top: -8px;" class="btn btn-success" data-toggle="modal" data-target="#exampleModalLongnewtest" onclick="setActionCreate('create_test');"><i class="fa fa-plus"></i></button>
|
||||
|
Loading…
Reference in New Issue
Block a user