Skip to content

Commit

Permalink
new platform
Browse files Browse the repository at this point in the history
  • Loading branch information
Aplet123 committed May 31, 2022
1 parent 27d02af commit c4ac8c7
Show file tree
Hide file tree
Showing 17 changed files with 400 additions and 890 deletions.
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/static/
Pipfile.lock
16 changes: 16 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM python:3.10-slim-bullseye

RUN apt-get update && apt-get install -y libpq-dev gcc
RUN pip install pipenv

WORKDIR /app
COPY Pipfile ./
RUN pipenv lock --pre --clear
RUN pipenv --three install --system --deploy --ignore-pipfile

COPY . .

ENV PORT=8080
EXPOSE 8080

CMD ["gunicorn", "-w", "4", "-k", "uvicorn.workers.UvicornWorker", "mbit.asgi:application"]
5 changes: 3 additions & 2 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]
ipython = "*"

[packages]
django = "*"
Expand All @@ -20,7 +21,7 @@ httptools = "*"
websockets = "*"
psycopg2 = "*"
flower = {file = "https://github.com/mher/flower/zipball/master"}
twisted = "==20.3.0"
twisted = "*"

[requires]
python_version = "3.9"
python_version = "3.10"
726 changes: 0 additions & 726 deletions Pipfile.lock

This file was deleted.

32 changes: 18 additions & 14 deletions dashboard/consumers.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def receive_json(self, content):
profile = Profile(division=Division.objects.get(id=content['division']), user=self.scope['user'], name=content['name'], members=json.dumps(cleaned), eligible=eligible)
profile.save()
else:
self.scope['user'].profile.division = Division.objects.get(id=content['division'])
# self.scope['user'].profile.division = Division.objects.get(id=content['division'])
self.scope['user'].profile.name = content['name']
self.scope['user'].profile.members = json.dumps(cleaned)
self.scope['user'].profile.eligible = eligible
Expand All @@ -137,21 +137,24 @@ def receive_json(self, content):
self.problems = Problem.objects.filter(rounds__division=self.division, rounds__start__lte=timezone.now(), rounds__end__gte=timezone.now())
self.send_profile()
elif content['type'] == 'get_problems':
ordering = ("index", "name") if self.division and self.division.name != "Advanced" else ("name",)
self.send_json({
'type': 'problems',
'problems': list(self.problems.order_by('id').values('name', 'slug'))
'problems': list(self.problems.order_by(*ordering).values('name', 'slug'))
})
elif content['type'] == 'get_problem' and 'slug' in content:
try: problem_obj = self.problems.get(slug=content['slug'])
except ObjectDoesNotExist: return
get_problem.apply_async(args=({
try:
problem_obj = self.problems.get(slug=content['slug'])
except ObjectDoesNotExist:
return
get_problem.apply_async(({
'type': 'problem',
'slug': content['slug'],
'user': self.scope['user'].id,
'channel': self.channel_name
},), queue='get_problem')
},), queue="get_problem")
elif content['type'] == 'get_test_case' and 'case' in content:
result_obj = TestCaseResult.objects.filter(id=content['case'], test_case__num__lte=4)
result_obj = TestCaseResult.objects.filter(id=content['case'], test_case__num__lte=1)
values = ('result', 'id', 'runtime')
kw_values = {'num': F('test_case__num')}
if len(result_obj) == 0:
Expand All @@ -164,7 +167,7 @@ def receive_json(self, content):
'type': 'case_result',
'case': list(result_obj.values(*values, **kw_values))[0]
})
elif content['type'] == 'submit' and 'problem' in content and 'submission' in content and content['submission'].get('filename') and content['submission'].get('language') in ('python', 'java', 'c++', 'pypy') and content['submission'].get('content'):
elif content['type'] == 'submit' and 'problem' in content and 'submission' in content and content['submission'].get('filename') and content['submission'].get('language') in ('py', 'java', 'cpp') and content['submission'].get('content'):
if len(content['submission']['content']) > 1000000:
self.send_json({'type': 'error', 'message': 'Submission too large.'})
return
Expand All @@ -186,22 +189,23 @@ def receive_json(self, content):
'time': int(submission.timestamp.timestamp()*1000)
}
})
grade.apply_async(args=({
grade.apply_async(({
'type': 'grade',
'problem': content['problem'],
'submission': submission.id,
'user_group': self.user_group,
'preliminary': True
},), queue='pretests')
},), queue="grade")
elif content['type'] == 'get_announcements':
self.send_announcements()
elif content['type'] == 'get_leaderboard' and 'division' in content:
get_leaderboard.apply_async(args=({
print("queued for celery")
get_leaderboard.apply_async(({
'type': 'leaderboard',
'division': content['division'],
'channel': self.channel_name,
'staff': self.scope['user'].is_staff
},), queue='leaderboard')
},), queue="get_leaderboard")
elif self.scope['user'].is_staff:
if content['type'] == 'admin_problems':
self.send_admin_problems()
Expand Down Expand Up @@ -245,10 +249,10 @@ def receive_json(self, content):
team = get_user_model().objects.get(username=content['team'])
problem = Problem.objects.get(slug=content['problem'])
submission = team.submission_set.filter(problem=problem).order_by('-timestamp').first()
grade.apply_async(args=({
grade.apply_async(({
'type': 'grade',
'problem': content['problem'],
'submission': submission.id,
'channel': self.channel_name,
'preliminary': False
},), queue='systemtests')
},), queue="grade")
53 changes: 28 additions & 25 deletions dashboard/management/commands/fix.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,32 @@
from django.core.management.base import BaseCommand

class Command(BaseCommand):
help = 'Rerun system tests that contained OOM (which only occurs when the grader bugs out)'
help = 'Rerun system tests that contained OOM (which only occurs when the grader bugs out)'

def handle(self, **kwargs):
for d in ('Advanced', 'Standard'):
try: division = Division.objects.get(name=d)
except ObjectDoesNotExist: return
teams = []
rounds = division.round_set.all()
for profile in division.profile_set.all():
team = {}
team['name'] = profile.name
team['eligible'] = profile.eligible
for round in rounds:
team['division'] = round.division.name
for problem in round.problem_set.all().order_by('id').only('name'):
try: submission = problem.submission_set.filter(user=profile.user).latest('timestamp')
except ObjectDoesNotExist: continue
if submission.testcaseresult_set.filter(result='memoryout', test_case__preliminary=False).count():
print(f"{team} failed")
submission.testcaseresult_set.filter(test_case__preliminary=False).delete()
grade.apply_async(args=({
'type': 'grade',
'problem': problem.slug,
'submission': submission.id,
'preliminary': False
},), queue='systemtests')
def handle(self, **kwargs):
for d in ('Advanced', 'Standard'):
try: division = Division.objects.get(name=d)
except ObjectDoesNotExist: return
teams = []
rounds = division.round_set.all()
for profile in division.profile_set.all():
team = {}
team['name'] = profile.name
team['eligible'] = profile.eligible
for round in rounds:
team['division'] = round.division.name
for problem in round.problem_set.all().order_by('id').only('name'):
try: submission = problem.submission_set.filter(user=profile.user).latest('timestamp')
except ObjectDoesNotExist: continue
tles = submission.testcaseresult_set.filter(result='timeout', test_case__preliminary=False).count()
if tles:
print(f"{team} failed {problem}: {tles} tles")
submission.testcaseresult_set.filter(test_case__preliminary=False).delete()
grade({
'type': 'grade',
'problem': problem.slug,
'submission': submission.id,
'preliminary': False
})
tles = submission.testcaseresult_set.filter(result='timeout', test_case__preliminary=False).count()
print(f"afterwards: {tles} tles")
4 changes: 2 additions & 2 deletions dashboard/management/commands/runsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ def handle(self, *args, **options):
problem = submission.problem
team = get_user_model().objects.get(username=submission.user.username)
submission = team.submission_set.filter(problem=problem).order_by('-timestamp').first()
grade.apply_async(args=({
grade.apply_async(({
'type': 'grade',
'problem': problem.slug,
'submission': submission.id,
'preliminary': False
},), queue='systemtests')
},), queue="grade")
18 changes: 18 additions & 0 deletions dashboard/migrations/0005_problem_index.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 4.0.4 on 2022-05-22 15:45

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('dashboard', '0004_testcaseresult_runtime'),
]

operations = [
migrations.AddField(
model_name='problem',
name='index',
field=models.IntegerField(default=0),
),
]
19 changes: 19 additions & 0 deletions dashboard/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,28 @@
class Division(models.Model):
name = models.CharField(max_length=64)

def __str__(self):
return f"Division ({self.id}): {self.name}"

class Profile(models.Model):
user = models.OneToOneField(get_user_model(), on_delete=models.CASCADE)
name = models.TextField(unique=True)
division = models.ForeignKey(Division, on_delete=models.CASCADE)
eligible = models.BooleanField(blank=True, null=True)
members = models.TextField(blank=True)

def __str__(self):
return f"Profile ({self.id}): {self.name}"

class Round(models.Model):
name = models.CharField(max_length=64)
division = models.ForeignKey(Division, on_delete=models.CASCADE)
start = models.DateTimeField()
end = models.DateTimeField()

def __str__(self):
return f"Round ({self.id}): {self.name}"

class Announcement(models.Model):
title = models.CharField(max_length=64)
content = models.TextField()
Expand All @@ -25,22 +34,32 @@ class Announcement(models.Model):
class TestCaseGroup(models.Model):
name = models.TextField(unique=True)

def __str__(self):
return f"TestCaseGroup ({self.id}): {self.name}"

class TestCase(models.Model):
num = models.IntegerField()
stdin = models.TextField()
stdout = models.TextField()
group = models.ForeignKey(TestCaseGroup, on_delete=models.CASCADE)
preliminary = models.BooleanField()

def __str__(self):
return f"TestCase ({self.id}): {self.group.name} #{self.num}, {'pretest' if self.preliminary else 'test'}"

class Problem(models.Model):
name = models.CharField(max_length=64)
slug = models.SlugField(max_length=64, unique=True)
index = models.IntegerField(default=0)
rounds = models.ManyToManyField(Round)
test_case_group = models.ForeignKey(TestCaseGroup, on_delete=models.PROTECT, null=True)
python_time = models.FloatField()
java_time = models.FloatField()
cpp_time = models.FloatField()

def __str__(self):
return f"Problem ({self.id}): {self.name}"

class Submission(models.Model):
code = models.TextField()
filename = models.TextField()
Expand Down
27 changes: 20 additions & 7 deletions dashboard/static/dashboard/js/dashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,30 @@ var descriptions = {
body: 'Your program exhausted the available memory (256 MB).'
},
'error': {
name: 'Error',
body: 'You may have a compile-time error or a run-time error. See below for more details.'
name: 'Runtime Error',
body: 'You have a run-time error. You can click the first pretest to view program output.'
},
'correct': {
name: 'Correct',
body: 'Nice job! Your program produced the correct output.'
},
'ctimeout': {
name: 'Compile Timeout',
body: 'Your program took too long to compile. If you\'re using C++, consider lowering header sizes.'
},
'cerror': {
name: 'Compilation Error',
body: 'Your program could not be compiled. You can click the first pretest to view compiler output.'
}
}

var codes = {
timeout: 'TLE',
incorrect: 'WA',
error: 'ERR',
memoryout: 'OOM'
memoryout: 'OOM',
ctimeout: 'CT',
cerror: 'CE'
}

var state = {
Expand Down Expand Up @@ -174,7 +184,7 @@ var problemPanel = new Vue({
},
submission: {
filename: '',
language: 'python',
language: 'py',
content: ''
},
submissionEditor: false,
Expand All @@ -196,7 +206,7 @@ var problemPanel = new Vue({
setFile: function (file) {
var language
this.submission.filename = file.name || ''
if (language = {"py": "python", "cpp": "c++", "java": "java"}[file.name.split('.').slice(-1)[0]]) {
if (language = {"py": "py", "cpp": "cpp", "java": "java"}[file.name.split('.').slice(-1)[0]]) {
this.submission.language = language
}
this.submissionEditor = false
Expand All @@ -207,7 +217,10 @@ var problemPanel = new Vue({
reader.readAsText(file)
},
submit () {
if (this.submission.content && !this.submission.filename) this.submission.filename = 'submission.'+{"python": "py", "pypy": "py", "c++": "cpp", "java": "java"}[this.submission.language]
if (!["py", "cpp", "java"].includes(this.submission.language)) {
return;
}
if (this.submission.content && !this.submission.filename) this.submission.filename = 'submission.'+{"py": "py", "cpp": "cpp", "java": "java"}[this.submission.language]
ws.send(JSON.stringify({'type': 'submit', 'submission': this.submission, 'problem': this.problem.slug}))
this.$refs.fileInput.value = null
this.submission.filename = ''
Expand Down Expand Up @@ -447,7 +460,7 @@ function connect () {
ws.addEventListener('close', function (event) {
openModal('disconnectedMessage')
announcementsPanel.first = true
if (connectTimeout === -1) connectTimeout = setTimeout(connect, 5000)
if (connectTimeout === -1) connectTimeout = setTimeout(connect, 3000)
})

}
Expand Down
Loading

0 comments on commit c4ac8c7

Please sign in to comment.