mirror of
https://gitlab.com/megazordpobeda/DataRush.git
synced 2026-05-23 03:57:09 +00:00
feat: added competition logic
This commit is contained in:
@@ -1,8 +1,9 @@
|
|||||||
|
from typing import Literal
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
|
||||||
from ninja import ModelSchema
|
from ninja import ModelSchema, Schema
|
||||||
|
|
||||||
from apps.competition.models import Competition
|
from apps.competition.models import Competition, State
|
||||||
|
|
||||||
|
|
||||||
class CompetitionOut(ModelSchema):
|
class CompetitionOut(ModelSchema):
|
||||||
@@ -12,12 +13,31 @@ class CompetitionOut(ModelSchema):
|
|||||||
model = Competition
|
model = Competition
|
||||||
fields = "__all__"
|
fields = "__all__"
|
||||||
|
|
||||||
|
class StateOut(ModelSchema):
|
||||||
|
class Meta:
|
||||||
|
model = State
|
||||||
|
fields = (
|
||||||
|
"state",
|
||||||
|
)
|
||||||
|
|
||||||
|
class StateIn(Schema):
|
||||||
|
state: Literal["started", "not_started", "finished"]
|
||||||
|
|
||||||
class CompetitionListInstanceOut(ModelSchema):
|
class CompetitionListInstanceOut(ModelSchema):
|
||||||
id: UUID
|
id: UUID
|
||||||
is_participating: bool
|
is_participating: bool
|
||||||
completed: bool
|
completed: bool
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_is_participating(self, context):
|
||||||
|
user = context["request"].auth
|
||||||
|
return self.participants.filter(id=user.id).exists()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def resolve_completed(self, context):
|
||||||
|
user = context["request"].auth
|
||||||
|
return State.objects.filter(competition=self, user=user, state="finished").exists()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Competition
|
model = Competition
|
||||||
fields = (
|
fields = (
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
from http import HTTPStatus as status
|
from http import HTTPStatus as status
|
||||||
|
from typing import Literal
|
||||||
from uuid import UUID
|
from uuid import UUID
|
||||||
|
from django.shortcuts import get_object_or_404
|
||||||
from django.http import HttpRequest
|
from django.http import HttpRequest, Http404
|
||||||
from ninja import Router
|
from ninja import Router
|
||||||
|
|
||||||
import api.v1.schemas as global_schemas
|
import api.v1.schemas as global_schemas
|
||||||
from api.v1.auth import BearerAuth
|
from api.v1.auth import BearerAuth
|
||||||
from api.v1.competition import schemas
|
from api.v1.competition import schemas
|
||||||
|
from apps.competition.models import Competition, State
|
||||||
|
|
||||||
router = Router(tags=["competition"])
|
router = Router(tags=["competition"])
|
||||||
|
|
||||||
@@ -18,11 +20,12 @@ router = Router(tags=["competition"])
|
|||||||
status.BAD_REQUEST: global_schemas.BadRequestError,
|
status.BAD_REQUEST: global_schemas.BadRequestError,
|
||||||
status.UNAUTHORIZED: global_schemas.UnauthorizedError,
|
status.UNAUTHORIZED: global_schemas.UnauthorizedError,
|
||||||
},
|
},
|
||||||
auth=BearerAuth(),
|
|
||||||
)
|
)
|
||||||
def get_competition(
|
def get_competition(
|
||||||
request: HttpRequest, competition_id: UUID
|
request: HttpRequest, competition_id: UUID
|
||||||
) -> tuple[status, schemas.CompetitionOut]: ...
|
) -> tuple[status, schemas.CompetitionOut]:
|
||||||
|
competition = get_object_or_404(Competition, id=competition_id)
|
||||||
|
return status.OK, competition
|
||||||
|
|
||||||
|
|
||||||
@router.get(
|
@router.get(
|
||||||
@@ -32,8 +35,36 @@ def get_competition(
|
|||||||
status.BAD_REQUEST: global_schemas.BadRequestError,
|
status.BAD_REQUEST: global_schemas.BadRequestError,
|
||||||
status.UNAUTHORIZED: global_schemas.UnauthorizedError,
|
status.UNAUTHORIZED: global_schemas.UnauthorizedError,
|
||||||
},
|
},
|
||||||
auth=BearerAuth(),
|
|
||||||
)
|
)
|
||||||
def list_competitions(
|
def list_competitions(
|
||||||
request: HttpRequest, is_participating: bool
|
request: HttpRequest, is_participating: bool
|
||||||
) -> tuple[status, list[schemas.CompetitionListInstanceOut]]: ...
|
) -> tuple[status, list[schemas.CompetitionListInstanceOut]]:
|
||||||
|
user = request.auth
|
||||||
|
if is_participating:
|
||||||
|
competitions = Competition.objects.filter(participants=user)
|
||||||
|
else:
|
||||||
|
competitions = Competition.objects.exclude(participants=user)
|
||||||
|
return status.OK, competitions
|
||||||
|
|
||||||
|
@router.post(
|
||||||
|
"competitions/{competition_id}/state",
|
||||||
|
response={
|
||||||
|
status.OK: schemas.StateOut,
|
||||||
|
status.BAD_REQUEST: global_schemas.BadRequestError,
|
||||||
|
status.UNAUTHORIZED: global_schemas.UnauthorizedError,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
def change_competition_state(
|
||||||
|
request: HttpRequest,
|
||||||
|
competition_id: UUID,
|
||||||
|
state: schemas.StateIn,
|
||||||
|
) -> tuple[status, schemas.StateOut]:
|
||||||
|
user = request.auth
|
||||||
|
competition = get_object_or_404(Competition, id=competition_id)
|
||||||
|
|
||||||
|
state_obj, _ = State.objects.update_or_create(
|
||||||
|
user=user,
|
||||||
|
competition=competition,
|
||||||
|
state=state.state
|
||||||
|
)
|
||||||
|
return status.OK, schemas.StateOut.from_orm(state_obj)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from functools import partial
|
|||||||
from ninja import NinjaAPI
|
from ninja import NinjaAPI
|
||||||
|
|
||||||
from api.v1 import handlers
|
from api.v1 import handlers
|
||||||
|
from api.v1.auth import BearerAuth
|
||||||
from api.v1.competition.views import router as competition_router
|
from api.v1.competition.views import router as competition_router
|
||||||
from api.v1.ping.views import router as ping_router
|
from api.v1.ping.views import router as ping_router
|
||||||
from api.v1.user.views import router as user_router
|
from api.v1.user.views import router as user_router
|
||||||
@@ -12,6 +13,7 @@ router = NinjaAPI(
|
|||||||
version="1",
|
version="1",
|
||||||
description="API docs for DataRush",
|
description="API docs for DataRush",
|
||||||
openapi_url="/docs/openapi.json",
|
openapi_url="/docs/openapi.json",
|
||||||
|
auth=BearerAuth()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 5.1.6 on 2025-02-28 22:40
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('competition', '0001_initial'),
|
||||||
|
('user', '0002_user_status'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='competition',
|
||||||
|
name='participants',
|
||||||
|
field=models.ManyToManyField(related_name='participants', to='user.user'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# Generated by Django 5.1.6 on 2025-02-28 23:26
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
import uuid
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('competition', '0002_competition_participants'),
|
||||||
|
('user', '0003_alter_user_status'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='State',
|
||||||
|
fields=[
|
||||||
|
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
|
||||||
|
('state', models.CharField(choices=[('not_started', 'Not Started'), ('started', 'Started'), ('finished', 'Finished')], max_length=11)),
|
||||||
|
('competition', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='competition.competition')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='user.user')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'abstract': False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -1,18 +1,18 @@
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
from apps.core.models import BaseModel
|
from apps.core.models import BaseModel
|
||||||
|
from apps.user.models import User
|
||||||
|
|
||||||
|
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"
|
||||||
|
|
||||||
|
|
||||||
class Competition(BaseModel):
|
|
||||||
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="Описание")
|
||||||
image_url = models.FileField(
|
image_url = models.FileField(
|
||||||
@@ -34,7 +34,19 @@ class Competition(BaseModel):
|
|||||||
choices=CompetitionParticipationType.choices,
|
choices=CompetitionParticipationType.choices,
|
||||||
verbose_name="Тип соревнования",
|
verbose_name="Тип соревнования",
|
||||||
)
|
)
|
||||||
|
participants = models.ManyToManyField(User, related_name="participants")
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = "соревнование"
|
verbose_name = "соревнование"
|
||||||
verbose_name_plural = "соревнования"
|
verbose_name_plural = "соревнования"
|
||||||
|
|
||||||
|
|
||||||
|
class State(BaseModel):
|
||||||
|
class StateChoices(models.TextChoices):
|
||||||
|
NOT_STARTED = 'not_started'
|
||||||
|
STARTED = 'started'
|
||||||
|
FINISHED = 'finished'
|
||||||
|
|
||||||
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
|
competition = models.ForeignKey(Competition, on_delete=models.CASCADE)
|
||||||
|
state = models.CharField(choices=StateChoices.choices, max_length=11)
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.1.6 on 2025-02-28 22:40
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('user', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='user',
|
||||||
|
name='status',
|
||||||
|
field=models.CharField(choices=[('student', 'Student'), ('metodist', 'Metodist')], default='student', max_length=10),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.1.6 on 2025-02-28 22:45
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('user', '0002_user_status'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='user',
|
||||||
|
name='status',
|
||||||
|
field=models.CharField(choices=[('student', 'Student'), ('metodist', 'Metodist')], default='student', max_length=10),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -13,7 +13,7 @@ class User(BaseModel):
|
|||||||
username = models.SlugField(unique=True, verbose_name="Юзернейм")
|
username = models.SlugField(unique=True, verbose_name="Юзернейм")
|
||||||
password = models.TextField(verbose_name="Пароль")
|
password = models.TextField(verbose_name="Пароль")
|
||||||
|
|
||||||
status = models.CharField(max_length=10, choices=UserRole.choices, default=UserRole.STUDENT)
|
status = models.CharField(max_length=10, choices=UserRole, default="student")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.username
|
return self.username
|
||||||
|
|||||||
Reference in New Issue
Block a user