Skip to content
This repository has been archived by the owner on Jan 3, 2024. It is now read-only.

Commit

Permalink
Merge pull request #26 from unb-mds/feature/problems
Browse files Browse the repository at this point in the history
[feature request] Adicionar app inicial de problemas
  • Loading branch information
bitterteriyaki authored Oct 5, 2023
2 parents 9860629 + cdd8cb6 commit 3ff43e8
Show file tree
Hide file tree
Showing 20 changed files with 393 additions and 110 deletions.
2 changes: 1 addition & 1 deletion apps/contests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@

class ContestsConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "appscontests"
name = "apps.contests"
16 changes: 2 additions & 14 deletions apps/contests/admin.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,10 @@
from typing import TYPE_CHECKING

from django.contrib.admin import ModelAdmin, register
from django.forms import (
CharField,
ModelForm,
ModelMultipleChoiceField,
Textarea,
)
from django.forms import CharField, ModelForm, Textarea
from django.utils.translation import gettext_lazy as _

from apps.contests.models import Contest
from apps.users.models import User

if TYPE_CHECKING:
ContestAdminBase = ModelAdmin[Contest]
Expand All @@ -22,9 +16,6 @@

class ContestModelForm(ContestModelFormBase):
description = CharField(widget=Textarea(attrs={"rows": 14, "cols": 80}))
users = ModelMultipleChoiceField(
queryset=User.objects.all(), required=False
)

class Meta:
model = Contest
Expand All @@ -40,8 +31,5 @@ class ContestAdmin(ContestAdminBase):

fieldsets = [
(_("General"), {"fields": ("title", "description")}),
(
_("Other"),
{"fields": ("start_time", "end_time", "users", "cancelled")},
),
(_("Other"), {"fields": ("start_time", "end_time", "cancelled")}),
]
4 changes: 1 addition & 3 deletions apps/contests/urls.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
from django.urls import path

from apps.contests.views import DetailView, IndexView, send
from apps.contests.views import DetailView

app_name = "contests"

urlpatterns = [
path("", IndexView.as_view(), name="index"),
path("<int:pk>/", DetailView.as_view(), name="detail"),
path("<int:contest_id>/send/", send, name="send"),
]
36 changes: 9 additions & 27 deletions apps/contests/views.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import sys
from io import StringIO
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Any

from django.db.models.query import QuerySet
from django.http import HttpRequest, HttpResponse
from django.shortcuts import get_object_or_404
from django.views import generic

from apps.contests.enums import ContestStatus
from apps.contests.models import Contest

if TYPE_CHECKING:
Expand All @@ -22,30 +19,15 @@ class IndexView(IndexViewBase):
context_object_name = "contests"

def get_queryset(self) -> QuerySet[Contest]:
return Contest._default_manager.order_by("-start_time")[:5]
return Contest._default_manager.order_by("start_time")[:5]

def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
ctx = super().get_context_data(**kwargs)
ctx["valid_statuses"] = (ContestStatus.PENDING, ContestStatus.RUNNING)

return ctx


class DetailView(DetailViewBase):
model = Contest
template_name = "contests/detail.html"


def send(request: HttpRequest, contest_id: int) -> HttpResponse:
contest = get_object_or_404(Contest, pk=contest_id)
code = request.POST["code"]

old_stdout = sys.stdout
sys.stdout = buffer = StringIO()

try:
eval(code)
except Exception as exc:
sys.stdout = old_stdout
return HttpResponse(f"Contest {contest.title} failed.\n{exc}")

sys.stdout = old_stdout
message = buffer.getvalue()

return HttpResponse(
f"Contest {contest.title} ran successfully.\n{message}"
)
8 changes: 8 additions & 0 deletions apps/problems/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from django.apps import AppConfig

default_app_config = "apps.problems.ProblemsConfig"


class ProblemsConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "apps.problems"
44 changes: 44 additions & 0 deletions apps/problems/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
from typing import TYPE_CHECKING

from django.contrib.admin import ModelAdmin, register
from django.forms import CharField, IntegerField, ModelForm, Textarea
from django.utils.translation import gettext_lazy as _

from apps.problems.models import Problem

if TYPE_CHECKING:
ProblemAdminBase = ModelAdmin[Problem]
ProblemModelFormBase = ModelForm[Problem]
else:
ProblemAdminBase = ModelAdmin
ProblemModelFormBase = ModelForm


class ProblemModelForm(ProblemModelFormBase):
description = CharField(widget=Textarea(attrs={"rows": 14, "cols": 80}))
score = IntegerField(min_value=0, required=False)

memory_limit = IntegerField(
min_value=0, required=False, help_text=_("In bytes.")
)
time_limit = IntegerField(
min_value=0, required=False, help_text=_("In seconds.")
)

class Meta:
model = Problem
fields = "__all__"


@register(Problem)
class ProblemAdmin(ProblemAdminBase):
form = ProblemModelForm

list_display = ("title", "contest", "memory_limit", "time_limit")
list_filter = ("contest", "score")

fieldsets = [
(_("General"), {"fields": ("title", "description")}),
(_("Meta"), {"fields": ("contest", "score")}),
(_("Limits"), {"fields": ("memory_limit", "time_limit")}),
]
45 changes: 45 additions & 0 deletions apps/problems/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Generated by Django 4.2.5 on 2023-10-03 12:48

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):
initial = True

dependencies = [
("contests", "0002_contest_cancelled"),
]

operations = [
migrations.CreateModel(
name="Problem",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("title", models.CharField(max_length=256)),
("description", models.CharField(max_length=4096)),
(
"contest",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="problems",
to="contests.contest",
),
),
],
options={
"ordering": ["-created_at", "-updated_at"],
"abstract": False,
},
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 4.2.5 on 2023-10-03 17:07

from django.db import migrations


class Migration(migrations.Migration):
dependencies = [
("problems", "0001_initial"),
]

operations = [
migrations.AlterModelOptions(
name="problem",
options={},
),
migrations.AlterModelTable(
name="problem",
table="problems",
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Generated by Django 4.2.5 on 2023-10-04 22:05

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("problems", "0002_alter_problem_options_alter_problem_table"),
]

operations = [
migrations.AddField(
model_name="problem",
name="memory_limit",
field=models.IntegerField(null=True),
),
migrations.AddField(
model_name="problem",
name="score",
field=models.IntegerField(null=True),
),
migrations.AddField(
model_name="problem",
name="time_limit",
field=models.IntegerField(null=True),
),
]
Empty file.
31 changes: 31 additions & 0 deletions apps/problems/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from django.db.models import CASCADE, CharField, ForeignKey, IntegerField

from apps.contests.enums import ContestStatus
from apps.contests.models import Contest
from core.models import TimestampedModel


class Problem(TimestampedModel):
"""Represents a problem in a contest."""

title = CharField(max_length=256)
description = CharField(max_length=4096)

contest = ForeignKey(Contest, related_name="problems", on_delete=CASCADE)
score = IntegerField(null=True)

memory_limit = IntegerField(null=True)
time_limit = IntegerField(null=True)

class Meta:
db_table = "problems"

def __str__(self) -> str:
return self.title

@property
def is_accessible(self) -> bool:
return self.contest.status in (
ContestStatus.RUNNING,
ContestStatus.FINISHED,
)
9 changes: 9 additions & 0 deletions apps/problems/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from django.urls import path

from apps.problems.views import DetailView

app_name = "problems"

urlpatterns = [
path("<int:pk>/", DetailView.as_view(), name="detail"),
]
15 changes: 15 additions & 0 deletions apps/problems/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from typing import TYPE_CHECKING

from django.views import generic

from apps.problems.models import Problem

if TYPE_CHECKING:
DetailViewBase = generic.DetailView[Problem]
else:
DetailViewBase = generic.DetailView


class DetailView(DetailViewBase):
model = Problem
template_name = "problems/detail.html"
2 changes: 2 additions & 0 deletions server/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"django.contrib.humanize",
]

THIRD_PARTY_APPS = [
Expand All @@ -78,6 +79,7 @@
LOCAL_APPS = [
"apps.users",
"apps.contests",
"apps.problems",
]

INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS
Expand Down
9 changes: 3 additions & 6 deletions server/urls.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
from django.contrib import admin
from django.urls import include, path
from django.views.generic import TemplateView

home_view = TemplateView.as_view(template_name="pages/home.html")
register_view = TemplateView.as_view(
template_name="registration/register.html"
)
from apps.contests.views import IndexView

urlpatterns = [
# Django views
path("admin/", admin.site.urls),
path("", include("django.contrib.auth.urls")),
# Local views
path("", home_view, name="home"),
path("", IndexView.as_view(), name="home"),
path("contests/", include("apps.contests.urls")),
path("problems/", include("apps.problems.urls")),
]
Loading

0 comments on commit 3ff43e8

Please sign in to comment.