mirror of
https://gitlab.com/megazordpobeda/DataRush.git
synced 2026-05-23 01:37:11 +00:00
Merge branch 'master' of https://gitlab.prodcontest.ru/team-15/project
This commit is contained in:
+1
-1
@@ -80,7 +80,7 @@ deploy:
|
|||||||
cd ~/deploy
|
cd ~/deploy
|
||||||
docker compose pull > deploy.log 2>&1
|
docker compose pull > deploy.log 2>&1
|
||||||
docker compose down >> deploy.log 2>&1
|
docker compose down >> deploy.log 2>&1
|
||||||
docker compose up -d --remove-orphans >> deploy.log 2>&1
|
docker compose up -d --remove-orphans --force-recreate >> deploy.log 2>&1
|
||||||
docker compose ps >> deploy.log 2>&1
|
docker compose ps >> deploy.log 2>&1
|
||||||
EOF
|
EOF
|
||||||
- ssh $SSH_ADDRESS "docker system prune -a --force"
|
- ssh $SSH_ADDRESS "docker system prune -a --force"
|
||||||
|
|||||||
+12
-3
@@ -3,6 +3,8 @@ name: datarush
|
|||||||
services:
|
services:
|
||||||
backend:
|
backend:
|
||||||
image: gitlab.prodcontest.ru:5050/team-15/project/backend:latest
|
image: gitlab.prodcontest.ru:5050/team-15/project/backend:latest
|
||||||
|
build:
|
||||||
|
context: ./services/backend
|
||||||
depends_on:
|
depends_on:
|
||||||
backend-initdb:
|
backend-initdb:
|
||||||
restart: false
|
restart: false
|
||||||
@@ -35,6 +37,8 @@ services:
|
|||||||
|
|
||||||
backend-initdb:
|
backend-initdb:
|
||||||
image: gitlab.prodcontest.ru:5050/team-15/project/backend:latest
|
image: gitlab.prodcontest.ru:5050/team-15/project/backend:latest
|
||||||
|
build:
|
||||||
|
context: ./services/backend
|
||||||
command: ./scripts/initdb
|
command: ./scripts/initdb
|
||||||
depends_on:
|
depends_on:
|
||||||
postgres:
|
postgres:
|
||||||
@@ -57,6 +61,9 @@ services:
|
|||||||
|
|
||||||
backend-staticfiles:
|
backend-staticfiles:
|
||||||
image: gitlab.prodcontest.ru:5050/team-15/project/backend-staticfiles:latest
|
image: gitlab.prodcontest.ru:5050/team-15/project/backend-staticfiles:latest
|
||||||
|
build:
|
||||||
|
context: ./services/backend
|
||||||
|
dockerfile: Dockerfile.staticfiles
|
||||||
env_file:
|
env_file:
|
||||||
- path: ./infrastructure/backend/.env.template
|
- path: ./infrastructure/backend/.env.template
|
||||||
required: true
|
required: true
|
||||||
@@ -79,6 +86,8 @@ services:
|
|||||||
|
|
||||||
backend-celery-worker:
|
backend-celery-worker:
|
||||||
image: gitlab.prodcontest.ru:5050/team-15/project/backend:latest
|
image: gitlab.prodcontest.ru:5050/team-15/project/backend:latest
|
||||||
|
build:
|
||||||
|
context: ./services/backend
|
||||||
command: celery -A config worker -l INFO
|
command: celery -A config worker -l INFO
|
||||||
depends_on:
|
depends_on:
|
||||||
redis:
|
redis:
|
||||||
@@ -307,12 +316,12 @@ services:
|
|||||||
- name: api
|
- name: api
|
||||||
target: 9000
|
target: 9000
|
||||||
published: 8005
|
published: 8005
|
||||||
host_ip: 127.0.0.1
|
host_ip: 0.0.0.0
|
||||||
protocol: tcp
|
protocol: tcp
|
||||||
- name: console
|
- name: console
|
||||||
target: 9001
|
target: 9001
|
||||||
published: 8006
|
published: 8006
|
||||||
host_ip: 127.0.0.1
|
host_ip: 0.0.0.0
|
||||||
protocol: tcp
|
protocol: tcp
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
@@ -338,7 +347,7 @@ services:
|
|||||||
- name: web
|
- name: web
|
||||||
target: 9090
|
target: 9090
|
||||||
published: 8007
|
published: 8007
|
||||||
host_ip: 127.0.0.1
|
host_ip: 0.0.0.0
|
||||||
protocol: tcp
|
protocol: tcp
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
shm_size: 4mb
|
shm_size: 4mb
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
DJANGO_SECRET_KEY=secretees
|
DJANGO_SECRET_KEY=secretees
|
||||||
DJANGO_DEBUG=False
|
DJANGO_DEBUG=False
|
||||||
DJANGO_ALLOWED_HOSTS=*
|
DJANGO_ALLOWED_HOSTS=*
|
||||||
DJANGO_CSRF_TRUSTED_ORIGINS=http://localhost,http://127.0.0.1
|
DJANGO_CSRF_TRUSTED_ORIGINS=http://localhost,http://127.0.0.1,https://*.final.prodcontest.ru
|
||||||
DJANGO_CORS_ALLOWED_ORIGINS=*
|
DJANGO_CORS_ALLOWED_ORIGINS=*
|
||||||
DJANGO_INTERNAL_IPS=127.0.0.1
|
DJANGO_INTERNAL_IPS=127.0.0.1
|
||||||
DJANGO_LANGUAGE_CODE=en-us
|
DJANGO_LANGUAGE_CODE=ru
|
||||||
DJANGO_STATIC_URL=http://localhost:13241/
|
DJANGO_STATIC_URL=static/
|
||||||
REDIS_URI=redis://redis:6379
|
REDIS_URI=redis://redis:6379
|
||||||
DJANGO_DB_URI=postgresql://postgres:postgres@postgres/postgres
|
DJANGO_DB_URI=postgresql://postgres:postgres@postgres/postgres
|
||||||
|
|
||||||
@@ -15,6 +15,6 @@ DJANGO_SUPERUSER_EMAIL=admin@mail.com
|
|||||||
DJANGO_SUPERUSER_PASSWORD=admin
|
DJANGO_SUPERUSER_PASSWORD=admin
|
||||||
|
|
||||||
MINIO_ENDPOINT=minio:9000
|
MINIO_ENDPOINT=minio:9000
|
||||||
MINIO_CUSTOM_ENDPOINT_URL=http://127.0.0.1:13244
|
MINIO_CUSTOM_ENDPOINT_URL=https://prod-team-15-minio-2pc0i3lc.final.prodcontest.ru
|
||||||
MINIO_ACCESS_KEY=admin
|
MINIO_ACCESS_KEY=admin
|
||||||
MINIO_SECRET_KEY=password
|
MINIO_SECRET_KEY=password
|
||||||
|
|||||||
@@ -48,10 +48,10 @@ domain = localhost
|
|||||||
enforce_domain = false
|
enforce_domain = false
|
||||||
|
|
||||||
# The full public facing url
|
# The full public facing url
|
||||||
root_url = %(protocol)s://%(domain)s:%(http_port)s/
|
root_url = https://prod-team-15-2pc0i3lc.final.prodcontest.ru/admin/grafana/
|
||||||
|
|
||||||
# Serve Grafana from subpath specified in `root_url` setting. By default it is set to `false` for compatibility reasons.
|
# Serve Grafana from subpath specified in `root_url` setting. By default it is set to `false` for compatibility reasons.
|
||||||
serve_from_sub_path = false
|
serve_from_sub_path = true
|
||||||
|
|
||||||
# Log web requests
|
# Log web requests
|
||||||
router_logging = false
|
router_logging = false
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
MINIO_ROOT_USER=admin
|
MINIO_ROOT_USER=admin
|
||||||
MINIO_ROOT_PASSWORD=password
|
MINIO_ROOT_PASSWORD=password
|
||||||
MINIO_VOLUMES=/data
|
MINIO_VOLUMES=/data
|
||||||
|
MINIO_BROWSER_REDIRECT_URL=https://prod-team-15-minio-2pc0i3lc.final.prodcontest.ru/minio/ui/
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ http {
|
|||||||
ssl_stapling_verify on;
|
ssl_stapling_verify on;
|
||||||
resolver 1.1.1.1 1.0.0.1 8.8.8.8 valid=300s;
|
resolver 1.1.1.1 1.0.0.1 8.8.8.8 valid=300s;
|
||||||
resolver_timeout 5s;
|
resolver_timeout 5s;
|
||||||
|
server_names_hash_bucket_size 128;
|
||||||
|
|
||||||
add_header X-Content-Type-Options "nosniff" always;
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
@@ -109,6 +110,25 @@ http {
|
|||||||
proxy_read_timeout 600s;
|
proxy_read_timeout 600s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location /static {
|
||||||
|
rewrite ^/static/(.*)$ /$1 break;
|
||||||
|
proxy_pass http://backend-staticfiles:80;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
proxy_cache_bypass $http_upgrade;
|
||||||
|
proxy_hide_header X-Powered-By;
|
||||||
|
|
||||||
|
proxy_connect_timeout 75s;
|
||||||
|
proxy_send_timeout 600s;
|
||||||
|
proxy_read_timeout 600s;
|
||||||
|
}
|
||||||
|
|
||||||
location /api {
|
location /api {
|
||||||
proxy_pass http://backend:8080;
|
proxy_pass http://backend:8080;
|
||||||
proxy_http_version 1.1;
|
proxy_http_version 1.1;
|
||||||
@@ -127,19 +147,89 @@ http {
|
|||||||
return 204;
|
return 204;
|
||||||
}
|
}
|
||||||
|
|
||||||
add_header 'Access-Control-Allow-Origin' "$http_origin" always;
|
proxy_buffering off;
|
||||||
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS' always;
|
proxy_request_buffering off;
|
||||||
add_header 'Access-Control-Allow-Headers' 'Authorization, Content-Type, X-Requested-With' always;
|
client_max_body_size 100M;
|
||||||
add_header 'Access-Control-Allow-Credentials' 'true' always;
|
}
|
||||||
|
|
||||||
|
location /admin {
|
||||||
|
proxy_pass http://backend:8080;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
proxy_buffering off;
|
proxy_buffering off;
|
||||||
proxy_request_buffering off;
|
proxy_request_buffering off;
|
||||||
client_max_body_size 100M;
|
client_max_body_size 100M;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
location /admin/grafana/ {
|
||||||
|
rewrite ^/admin/grafana/(.*) /$1 break;
|
||||||
|
proxy_pass http://grafana:3000/;
|
||||||
|
|
||||||
|
proxy_set_header Host $http_host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
|
||||||
error_page 404 = @notfound;
|
error_page 404 = @notfound;
|
||||||
location @notfound {
|
location @notfound {
|
||||||
return 444;
|
return 444;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 443 ssl http2;
|
||||||
|
listen [::]:443 ssl http2;
|
||||||
|
server_name prod-team-15-minio-2pc0i3lc.final.prodcontest.ru;
|
||||||
|
|
||||||
|
ssl_certificate /etc/nginx/certs/fullchain.pem;
|
||||||
|
ssl_certificate_key /etc/nginx/certs/privkey.pem;
|
||||||
|
|
||||||
|
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||||
|
add_header X-Content-Type-Options "nosniff" always;
|
||||||
|
|
||||||
|
ignore_invalid_headers off;
|
||||||
|
client_max_body_size 0;
|
||||||
|
proxy_buffering off;
|
||||||
|
proxy_request_buffering off;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_set_header Host $http_host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
|
||||||
|
proxy_connect_timeout 300;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
chunked_transfer_encoding off;
|
||||||
|
|
||||||
|
proxy_pass http://minio:9000;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /minio/ui/ {
|
||||||
|
rewrite ^/minio/ui/(.*) /$1 break;
|
||||||
|
proxy_set_header Host $http_host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
proxy_set_header X-NginX-Proxy true;
|
||||||
|
|
||||||
|
real_ip_header X-Real-IP;
|
||||||
|
|
||||||
|
proxy_connect_timeout 300;
|
||||||
|
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Upgrade $http_upgrade;
|
||||||
|
proxy_set_header Connection "upgrade";
|
||||||
|
|
||||||
|
chunked_transfer_encoding off;
|
||||||
|
|
||||||
|
proxy_pass http://minio:9001;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,6 +37,6 @@ ENV PYTHONDONTWRITEBYTECODE=1 \
|
|||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
|
|
||||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --start-interval=2s --retries=3 \
|
HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --start-interval=2s --retries=3 \
|
||||||
CMD wget --no-verbose --tries=1 --spider http://127.0.0.1:8080/health?format=json || exit 1
|
CMD wget --no-verbose --tries=1 --spider http://127.0.0.1:8080/api/health?format=json || exit 1
|
||||||
|
|
||||||
CMD gunicorn config.wsgi --workers=8 -b 0.0.0.0:8080 --access-logfile - --error-logfile -
|
CMD gunicorn config.wsgi --workers=8 -b 0.0.0.0:8080 --access-logfile - --error-logfile - --timeout=600
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from apps.competition.models import Competition
|
||||||
|
from apps.task.admin import CompetitionTaskInline
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(Competition)
|
||||||
|
class CompetitionAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ("title", "end_date", "type",)
|
||||||
|
search_fields = ("title", "description",)
|
||||||
|
list_filter = ("type", "participation_type",)
|
||||||
|
inlines = [CompetitionTaskInline]
|
||||||
@@ -4,3 +4,4 @@ from django.apps import AppConfig
|
|||||||
class CompetitionsConfig(AppConfig):
|
class CompetitionsConfig(AppConfig):
|
||||||
name = "apps.competition"
|
name = "apps.competition"
|
||||||
label = "competition"
|
label = "competition"
|
||||||
|
verbose_name = "Соревнование"
|
||||||
|
|||||||
+35
@@ -0,0 +1,35 @@
|
|||||||
|
# Generated by Django 5.1.6 on 2025-03-01 12:21
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('competition', '0001_initial'),
|
||||||
|
('task', '0001_initial'),
|
||||||
|
('user', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='competition',
|
||||||
|
name='tasks',
|
||||||
|
field=models.ManyToManyField(blank=True, related_name='tasks', to='task.competitiontask'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='competition',
|
||||||
|
name='participants',
|
||||||
|
field=models.ManyToManyField(blank=True, editable=False, related_name='participants', to='user.user'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='competition',
|
||||||
|
name='participation_type',
|
||||||
|
field=models.CharField(choices=[('edu', 'Образовательный'), ('competitive', 'Соревновательный')], max_length=11, verbose_name='Тип соревнования'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='competition',
|
||||||
|
name='type',
|
||||||
|
field=models.CharField(choices=[('solo', 'Индивидуальный')], max_length=10, verbose_name='Тип участия'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -8,11 +8,11 @@ from apps.user.models import User
|
|||||||
|
|
||||||
class Competition(BaseModel):
|
class Competition(BaseModel):
|
||||||
class CompetitionType(models.TextChoices):
|
class CompetitionType(models.TextChoices):
|
||||||
SOLO = "solo"
|
SOLO = "solo", "Индивидуальный"
|
||||||
|
|
||||||
class CompetitionParticipationType(models.TextChoices):
|
class CompetitionParticipationType(models.TextChoices):
|
||||||
EDU = "edu"
|
EDU = "edu", "Образовательный"
|
||||||
COMPETITIVE = "competitive"
|
COMPETITIVE = "competitive", "Соревновательный"
|
||||||
|
|
||||||
title = models.CharField(max_length=100, verbose_name="Название")
|
title = models.CharField(max_length=100, verbose_name="Название")
|
||||||
description = models.TextField(verbose_name="Описание")
|
description = models.TextField(verbose_name="Описание")
|
||||||
@@ -35,7 +35,11 @@ class Competition(BaseModel):
|
|||||||
choices=CompetitionParticipationType.choices,
|
choices=CompetitionParticipationType.choices,
|
||||||
verbose_name="Тип соревнования",
|
verbose_name="Тип соревнования",
|
||||||
)
|
)
|
||||||
participants = models.ManyToManyField(User, related_name="participants")
|
participants = models.ManyToManyField(User, related_name="participants", blank=True,
|
||||||
|
editable=False)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.title
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = "соревнование"
|
verbose_name = "соревнование"
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
from django.contrib.auth.models import Group, User
|
||||||
|
|
||||||
|
admin.site.unregister(Group)
|
||||||
|
admin.site.unregister(User)
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
from apps.task.models import CompetitionTask
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(CompetitionTask)
|
||||||
|
class CompetitionTaskAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ("title", "type", "points")
|
||||||
|
|
||||||
|
|
||||||
|
class CompetitionTaskInline(admin.StackedInline):
|
||||||
|
model = CompetitionTask
|
||||||
|
extra = 0
|
||||||
@@ -4,3 +4,4 @@ from django.apps import AppConfig
|
|||||||
class CompetitionsConfig(AppConfig):
|
class CompetitionsConfig(AppConfig):
|
||||||
name = "apps.task"
|
name = "apps.task"
|
||||||
label = "task"
|
label = "task"
|
||||||
|
verbose_name = "Задания"
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
# Generated by Django 5.1.6 on 2025-03-01 12:21
|
||||||
|
|
||||||
|
import apps.task.models
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('competition', '0002_competition_tasks_alter_competition_participants_and_more'),
|
||||||
|
('task', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='competitiontask',
|
||||||
|
options={'verbose_name': 'задание', 'verbose_name_plural': 'задания'},
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='competitiontask',
|
||||||
|
name='competition',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='competition.competition', verbose_name='соревнование'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='competitiontask',
|
||||||
|
name='correct_answer_file',
|
||||||
|
field=models.FileField(blank=True, null=True, upload_to=apps.task.models.CompetitionTask.answer_file_upload_to, verbose_name='правильный ответ'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='competitiontask',
|
||||||
|
name='criteries',
|
||||||
|
field=models.JSONField(blank=True, null=True, verbose_name='критерии проверки'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='competitiontask',
|
||||||
|
name='points',
|
||||||
|
field=models.IntegerField(blank=True, null=True, verbose_name='баллы за задание'),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='competitiontask',
|
||||||
|
name='type',
|
||||||
|
field=models.CharField(choices=[('input', 'Input'), ('checker', 'Checker'), ('review', 'Review')], max_length=8, verbose_name='тип задания'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -10,33 +10,66 @@ from apps.user.models import User
|
|||||||
|
|
||||||
class CompetitionTask(BaseModel):
|
class CompetitionTask(BaseModel):
|
||||||
class CompetitionTaskType(models.TextChoices):
|
class CompetitionTaskType(models.TextChoices):
|
||||||
INPUT = "input"
|
INPUT = "input", "Ввод правильного ответа"
|
||||||
CHECKER = "checker"
|
CHECKER = "checker", "Вывод кода"
|
||||||
REVIEW = "review"
|
REVIEW = "review", "Ручная"
|
||||||
|
|
||||||
def answer_file_upload_to(instance, filename) -> str:
|
def answer_file_upload_to(instance, filename) -> str:
|
||||||
return f"/tasks/{instance.id}/answer/{uuid4()}/filename"
|
return f"/tasks/{instance.id}/answer/{uuid4()}/filename"
|
||||||
|
|
||||||
competition = models.ForeignKey(Competition, on_delete=models.CASCADE)
|
competition = models.ForeignKey(Competition, on_delete=models.CASCADE)
|
||||||
title = models.TextField(verbose_name="заголовок", max_length=50)
|
title = models.CharField(verbose_name="заголовок", max_length=50)
|
||||||
description = models.TextField(verbose_name="описание", max_length=300)
|
description = models.TextField(verbose_name="описание", max_length=300)
|
||||||
type = models.CharField(choices=CompetitionTaskType, max_length=8)
|
max_attemps = models.PositiveSmallIntegerField()
|
||||||
|
type = models.CharField(
|
||||||
|
choices=CompetitionTaskType, max_length=8, verbose_name="тип проверки"
|
||||||
|
)
|
||||||
|
|
||||||
# only when "input" or "checker" type
|
# only when "input" or "checker" type
|
||||||
correct_answer_file = models.FileField(
|
correct_answer_file = models.FileField(
|
||||||
upload_to=answer_file_upload_to, null=True, blank=True
|
upload_to=answer_file_upload_to,
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
verbose_name="файл с правильным ответом",
|
||||||
|
)
|
||||||
|
points = models.IntegerField(
|
||||||
|
null=True, blank=True, verbose_name="баллы за задание"
|
||||||
)
|
)
|
||||||
points = models.IntegerField(null=True, blank=True)
|
|
||||||
|
|
||||||
# only when "checker" type
|
# only when "checker" type
|
||||||
answer_file_path = models.TextField(null=True, blank=True)
|
answer_file_path = models.TextField(
|
||||||
|
null=True,
|
||||||
|
blank=True,
|
||||||
|
verbose_name="куда сохранять решения",
|
||||||
|
default="stdout",
|
||||||
|
)
|
||||||
|
|
||||||
# only when "review" type
|
# only when "review" type
|
||||||
criteries = models.JSONField(blank=True, null=True)
|
# todo make it more humanize
|
||||||
|
criteries = models.JSONField(
|
||||||
|
blank=True,
|
||||||
|
null=True,
|
||||||
|
verbose_name="критерии",
|
||||||
|
default=lambda: [
|
||||||
|
{
|
||||||
|
"name": "CHANGE ME",
|
||||||
|
"slug": "CHANGE ME",
|
||||||
|
"max_value": 0,
|
||||||
|
"min_value": 0,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
ContestTaskCriteriesValidator()(self)
|
ContestTaskCriteriesValidator()(self)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.title
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "задание"
|
||||||
|
verbose_name_plural = "задания"
|
||||||
|
|
||||||
|
|
||||||
class CompetetionTaskSumbission(BaseModel):
|
class CompetetionTaskSumbission(BaseModel):
|
||||||
class StatusChoices(models.TextChoices):
|
class StatusChoices(models.TextChoices):
|
||||||
|
|||||||
@@ -227,7 +227,7 @@ FIRST_DAY_OF_WEEK = 1
|
|||||||
|
|
||||||
FORMAT_MODULE_PATH = None
|
FORMAT_MODULE_PATH = None
|
||||||
|
|
||||||
LANGUAGE_CODE = env("DJANGO_LANGUAGE_CODE", default="en-us")
|
LANGUAGE_CODE = env("DJANGO_LANGUAGE_CODE", default="ru-ru")
|
||||||
|
|
||||||
LANGUAGES = [("en", _("English")), ("ru", _("Russian"))]
|
LANGUAGES = [("en", _("English")), ("ru", _("Russian"))]
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ dependencies = [
|
|||||||
"psycopg2-binary>=2.9.10",
|
"psycopg2-binary>=2.9.10",
|
||||||
"pydantic>=2.10.5",
|
"pydantic>=2.10.5",
|
||||||
"pyjwt>=2.10.1",
|
"pyjwt>=2.10.1",
|
||||||
|
"python-gettext>=5.0",
|
||||||
"python-json-logger>=3.2.1",
|
"python-json-logger>=3.2.1",
|
||||||
"pytz>=2024.2",
|
"pytz>=2024.2",
|
||||||
"redis>=5.2.1",
|
"redis>=5.2.1",
|
||||||
|
|||||||
Reference in New Issue
Block a user