Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(BE): Temperature model #358

Open
wants to merge 46 commits into
base: dev/user-profile
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
991698b
feat: Add and link user profile page
jimin9038 Jan 8, 2022
3e669bf
feat: initialize user profile page
jimin9038 Jan 9, 2022
4f2e15a
Initialize Group Page.
DailyPS Feb 1, 2022
821dd0d
Change CSS.
DailyPS Feb 1, 2022
56d2011
change section title to common css
jimin9038 Feb 1, 2022
d48b8e6
Add Profile Submission in Profile Page (#290)
st42597 Feb 1, 2022
1788c95
Add profileSubmissionListAPI in Profile Page (#308)
st42597 Mar 1, 2022
f7a40bf
refactor: Rebase
goo314 Jan 10, 2022
898e0ab
fix: Delete routers of profile-contest
goo314 Jan 11, 2022
91b3329
feat: Add ProfileContest vue
goo314 Jan 11, 2022
ee6d032
feat: Publish user contest page
goo314 Jan 14, 2022
0ed9221
add: Add file in gitignore
goo314 Jan 14, 2022
3b39801
feat: Add Pagination component
goo314 Jan 21, 2022
5c816f3
feat: Add methods for each button in pagination vue
goo314 Jan 24, 2022
31cddcf
chore: Remove chart.js outside frontend folder
goo314 Jan 28, 2022
90ef7c8
fix: Show at most limit pages
goo314 Jan 28, 2022
d60dd19
style: Shorten style code
goo314 Jan 28, 2022
48c4bdc
fix: Add component communication(props, event)
goo314 Jan 28, 2022
fedb98c
style: Apply Pagination vue
goo314 Jan 28, 2022
2f2c883
style: Edit code style
goo314 Feb 6, 2022
0d99131
feat: Create USerContestAPI
goo314 Feb 8, 2022
4b1bad0
add: Calate and return user rank in UserContestAPI
goo314 Feb 9, 2022
679503c
style: Change code style
goo314 Feb 10, 2022
56326a5
add: Add rank field in ACMContestRank & Shorten UserContestAPI
goo314 Feb 10, 2022
4d10173
add: Connect UserContestAPI to ProfileContest page
goo314 Feb 14, 2022
4dce664
feat: Add sorting contests function in ProfileContest
goo314 Feb 20, 2022
48b4a94
fix: Connect sorting api to ProfileContest
goo314 Feb 20, 2022
1787bcb
feat: Add test for user-contest-api
goo314 Feb 26, 2022
bdf081e
feat: add contestprize model
jimin9038 Feb 25, 2022
2b67eea
style: Change code style
goo314 Mar 1, 2022
fa65414
migrate model change
jimin9038 Mar 2, 2022
ead63b1
resolve conflict: whiel rebase on master
jimin9038 Mar 2, 2022
bc3dff8
Fix UserContestAPITest
jimin9038 Mar 2, 2022
7dd4222
minor design change followed by tailwind css
jimin9038 Mar 2, 2022
1933ea5
init: temperature
st42597 Mar 15, 2022
6ee2690
fix: minor fix & add: rankAPI
st42597 Mar 15, 2022
51f9849
fix: foreign key
st42597 Mar 23, 2022
067f949
add: model migration
st42597 Mar 23, 2022
fdaacb3
fix: remigrate temperature model changes
jimin9038 Mar 23, 2022
652369d
fix: code style
st42597 Mar 23, 2022
c1cbe39
init: history
st42597 Mar 28, 2022
c47a7b4
fix: temperature
st42597 Apr 20, 2022
e19539a
add: temperature api
st42597 Apr 20, 2022
c43a561
add: new date temperature calculator
st42597 Apr 21, 2022
ac50b4d
fix: temperature calculate
st42597 Apr 26, 2022
f7d1d18
fix: delete trash file
st42597 Apr 26, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .DS_Store
Binary file not shown.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -196,3 +196,6 @@ typings/

# devcontainer
.devcontainer/data

# MacOS file
.DS_Store
Binary file added backend/.DS_Store
Binary file not shown.
49 changes: 49 additions & 0 deletions backend/contest/migrations/0014_auto_20220302_1508.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Generated by Django 3.2.12 on 2022-03-02 06:08

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


class Migration(migrations.Migration):

dependencies = [
('contest', '0013_contestannouncement_problem'),
]

operations = [
migrations.AddField(
model_name='contest',
name='constraints',
field=models.JSONField(default=list),
),
migrations.AddField(
model_name='contest',
name='requirements',
field=models.JSONField(default=list),
),
migrations.AddField(
model_name='contest',
name='scoring',
field=models.TextField(default='ACM-ICPC style'),
),
migrations.CreateModel(
name='ContestPrize',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('color', models.TextField()),
('name', models.TextField()),
('reward', models.TextField()),
('contest', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contest.contest')),
],
),
migrations.AddField(
model_name='acmcontestrank',
name='prize',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='contest.contestprize'),
),
migrations.AddField(
model_name='oicontestrank',
name='prize',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='contest.contestprize'),
),
]
25 changes: 25 additions & 0 deletions backend/contest/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,16 @@
from account.models import User
from utils.models import RichTextField

from account.models import AdminType


class Contest(models.Model):
title = models.TextField()
description = RichTextField()
requirements = JSONField(default=list)
constraints = JSONField(default=list)
# allowed_groups = models.ManyToManyField(Group, blank=True)
scoring = models.TextField(default="ACM-ICPC style")
# show real time rank or cached rank
real_time_rank = models.BooleanField()
password = models.TextField(null=True)
Expand Down Expand Up @@ -55,10 +61,18 @@ class Meta:
ordering = ("-start_time",)


class ContestPrize(models.Model):
contest = models.ForeignKey(Contest, on_delete=models.CASCADE)
color = models.TextField()
name = models.TextField()
reward = models.TextField()


class AbstractContestRank(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
contest = models.ForeignKey(Contest, on_delete=models.CASCADE)
submission_number = models.IntegerField(default=0)
prize = models.ForeignKey(ContestPrize, on_delete=models.SET_NULL, blank=True, null=True)

class Meta:
abstract = True
Expand All @@ -73,6 +87,17 @@ class ACMContestRank(AbstractContestRank):
# key is problem id
submission_info = JSONField(default=dict)

@property
def rank(self):
qs_contest = Contest.objects.get(id=self.contest.id)
if qs_contest.status == ContestStatus.CONTEST_ENDED:
qs_participants = ACMContestRank.objects.filter(contest=self.contest.id, user__admin_type=AdminType.REGULAR_USER, user__is_disabled=False).\
select_related("user").order_by("-accepted_number", "total_penalty", "total_time")
for i in range(qs_participants.count()):
if qs_participants[i].user.id == self.user.id:
return i+1
return -1

class Meta:
db_table = "acm_contest_rank"
unique_together = (("user", "contest"),)
Expand Down
8 changes: 8 additions & 0 deletions backend/contest/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,11 @@ class ACMContesHelperSerializer(serializers.Serializer):
problem_id = serializers.CharField()
rank_id = serializers.IntegerField()
checked = serializers.BooleanField()


class ProfileContestSerializer(serializers.Serializer):
id = serializers.IntegerField()
title = serializers.CharField(max_length=128)
start_time = serializers.DateTimeField()
rank = serializers.IntegerField()
percentage = serializers.FloatField()
64 changes: 61 additions & 3 deletions backend/contest/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

from utils.api.tests import APITestCase

from .models import ContestAnnouncement, ContestRuleType, Contest

from problem.models import ProblemIOMode
from .models import ContestAnnouncement, ContestRuleType, Contest, ACMContestRank
from submission.models import Submission
from problem.models import Problem, ProblemIOMode, ProblemTag

DEFAULT_PROBLEM_DATA = {"_id": "A-110", "title": "test", "description": "<p>test</p>", "input_description": "test",
"output_description": "test", "time_limit": 1000, "memory_limit": 256, "difficulty": "Level1",
Expand All @@ -29,6 +29,22 @@
"allowed_ip_ranges": [],
"visible": True, "real_time_rank": True}

DEFAULT_SUBMISSION_DATA = {
"problem_id": "1",
"user_id": 1,
"username": "test",
"code": "xxxxxxxxxxxxxx",
"result": -2,
"info": {},
"language": "C",
"statistic_info": {}
}


DEFAULT_ACMCONTESTRANK_DATA = {"submission_number": 1, "accepted_number": 1, "total_time": 123, "total_penalty": 123,
"submission_info": {"1": {"is_ac": True, "ac_time": 123, "penalty": 123, "problem_submission": 1}},
"contest": 1}


class ContestAdminAPITest(APITestCase):
def setUp(self):
Expand Down Expand Up @@ -167,3 +183,45 @@ def test_get_contest_announcement_list(self):
contest_id = self.create_contest_announcements()
response = self.client.get(self.url, data={"contest_id": contest_id})
self.assertSuccess(response)


class UserContestAPITest(APITestCase):
def setUp(self):
# create contest
admin = self.create_admin()
self.contest = Contest.objects.create(created_by=admin, **DEFAULT_CONTEST_DATA)

# create problem in contest
data = copy.deepcopy(DEFAULT_PROBLEM_DATA)
data["contest_id"] = self.contest.id
tags = data.pop("tags")
problem = Problem.objects.create(created_by=admin, **data)

for item in tags:
try:
tag = ProblemTag.objects.get(name=item)
except ProblemTag.DoesNotExist:
tag = ProblemTag.objects.create(name=item)
problem.tags.add(tag)

self.problem = problem
# user submit problem
user = self.create_user("test", "test123")
data = copy.deepcopy(DEFAULT_SUBMISSION_DATA)
data["contest_id"] = self.contest.id
data["problem_id"] = self.problem.id
data["user_id"] = user.id
self.submission = Submission.objects.create(**data)

# create ACMContestRank
data = copy.deepcopy(DEFAULT_ACMCONTESTRANK_DATA)
data["user"] = user
data["contest"] = self.contest
self.rank = ACMContestRank.objects.create(**data)

self.url = self.reverse("contest_user_api")

# test UserContestAPI : can user get contest info which he participated and rank?
def test_get_participated_contest_list(self):
response = self.client.get(self.url)
self.assertSuccess(response)
2 changes: 2 additions & 0 deletions backend/contest/urls/oj.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from ..views.oj import ContestAnnouncementListAPI
from ..views.oj import ContestPasswordVerifyAPI, ContestAccessAPI
from ..views.oj import ContestListAPI, ContestAPI, ContestRankAPI
from ..views.oj import UserContestAPI

urlpatterns = [
path("contests/", ContestListAPI.as_view(), name="contest_list_api"),
Expand All @@ -11,4 +12,5 @@
path("contest/announcement/", ContestAnnouncementListAPI.as_view(), name="contest_announcement_api"),
path("contest/access/", ContestAccessAPI.as_view(), name="contest_access_api"),
path("contest/rank/", ContestRankAPI.as_view(), name="contest_rank_api"),
path("contest/user/", UserContestAPI.as_view(), name="contest_user_api"),
]
27 changes: 27 additions & 0 deletions backend/contest/views/oj.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from ..serializers import ContestAnnouncementSerializer
from ..serializers import ContestSerializer, ContestPasswordVerifySerializer
from ..serializers import ACMContestRankSerializer
from ..serializers import ProfileContestSerializer


class ContestAnnouncementListAPI(APIView):
Expand Down Expand Up @@ -229,3 +230,29 @@ def get(self, request):
page_qs["results"] = serializer(page_qs["results"], many=True, is_contest_admin=is_contest_admin).data
page_qs["results"].append(self.contest.id)
return self.success(page_qs)


class UserContestAPI(APIView):
def get(self, request):
user = request.user
# queryset for all problems information which user submitted
qs_problems = ACMContestRank.objects.filter(user=user.id, user__admin_type=AdminType.REGULAR_USER, user__is_disabled=False)
contests = []
for problem in qs_problems:
contest_id = problem.contest.id
try:
contest = Contest.objects.get(id=contest_id, visible=True)
contest = ContestSerializer(contest).data
total_participants = ACMContestRank.objects.filter(contest=contest_id, user__admin_type=AdminType.REGULAR_USER, user__is_disabled=False).count()
contest["rank"] = problem.rank
contest["percentage"] = round(contest["rank"]/total_participants*100, 2)
contests.append(contest)
except Contest.DoesNotExist:
return self.error("Contest does not exist")

# priority
priority = request.GET.get("priority")
if priority:
contests = sorted(contests, key=lambda c: c[priority])

return self.success(ProfileContestSerializer(contests, many=True).data)
1 change: 1 addition & 0 deletions backend/oj/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
'contest',
'utils',
'submission',
'temperature',
'options',
'judge',
'assignment',
Expand Down
1 change: 1 addition & 0 deletions backend/oj/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@
path("api/lecture/professor/", include("course.urls.professor")),
path("api/lecture/", include("assignment.urls.student")),
path("api/lecture/professor/", include("assignment.urls.professor")),
path("api/", include("temperature.urls")),
]
23 changes: 23 additions & 0 deletions backend/submission/migrations/0017_auto_20220302_1508.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 3.2.12 on 2022-03-02 06:08

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('submission', '0016_auto_20211226_1458'),
]

operations = [
migrations.AddField(
model_name='submission',
name='code_length',
field=models.IntegerField(default=0),
),
migrations.AddField(
model_name='submission',
name='title',
field=models.TextField(null=True),
),
]
2 changes: 2 additions & 0 deletions backend/submission/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,12 @@ class Submission(models.Model):
contest = models.ForeignKey(Contest, null=True, on_delete=models.CASCADE)
problem = models.ForeignKey(Problem, on_delete=models.CASCADE)
assignment = models.ForeignKey(Assignment, null=True, on_delete=models.CASCADE, related_name="submissions")
title = models.TextField(null=True)
create_time = models.DateTimeField(auto_now_add=True)
user_id = models.IntegerField(db_index=True)
username = models.TextField()
code = models.TextField()
code_length = models.IntegerField(default=0)
result = models.IntegerField(db_index=True, default=JudgeStatus.PENDING)
# Judgment details returned from JudgeServer
info = JSONField(default=dict)
Expand Down
2 changes: 2 additions & 0 deletions backend/submission/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from .views import (SubmissionAPI, SubmissionListAPI, ContestSubmissionListAPI, AssignmentSubmissionListAPI,
AssignmentSubmissionListProfessorAPI, SubmissionExistsAPI, EditSubmissionScoreAPI)
from .views import ProfileSubmissionListAPI

urlpatterns = [
path("submission/", SubmissionAPI.as_view(), name="submission_api"),
Expand All @@ -11,4 +12,5 @@
path("assignment_submissions/", AssignmentSubmissionListAPI.as_view(), name="assignment_submission_list_api"),
path("assignment_submissions_professor/", AssignmentSubmissionListProfessorAPI.as_view(), name="assignment_submission_list_professor_api"),
path("edit_submission_score/", EditSubmissionScoreAPI.as_view(), name="edit_submission_score_api"),
path("profile_submissions/", ProfileSubmissionListAPI.as_view(), name="profile_submission_list_api"),
]
22 changes: 22 additions & 0 deletions backend/submission/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
# from judge.dispatcher import JudgeDispatcher
from problem.models import Problem, ProblemRuleType
from account.models import User, AdminType
from django.db.models import Q
from utils.api import APIView, validate_serializer
from utils.constants import AssignmentStatus
from utils.cache import cache
Expand Down Expand Up @@ -86,7 +87,9 @@ def post(self, request):
username=request.user.username,
language=data["language"],
code=data["code"],
code_length=len(data["code"].encode("utf-8")),
problem_id=problem.id,
title=problem.title,
ip=request.session["ip"],
contest_id=data.get("contest_id"),
assignment_id=data.get("assignment_id"))
Expand Down Expand Up @@ -473,3 +476,22 @@ def get(self, request):
return self.success(request.user.is_authenticated and
Submission.objects.filter(problem_id=request.GET["problem_id"],
user_id=request.user.id).exists())


class ProfileSubmissionListAPI(APIView):
def get(self, request):
submissions = Submission.objects.filter(user_id=request.user.id)
# keyword search, 문제 번호로 검색 안됨
keyword = request.GET.get("keyword", "").strip()
if keyword:
submissions = submissions.filter(Q(title__icontains=keyword))
# result search
result = request.GET.get("result")
if result:
if result == "AC":
submissions = submissions.filter(result=0)
elif result == "NAC":
submissions = submissions.exclude(result=0)
data = self.paginate_data(request, submissions)
data["results"] = SubmissionListSerializer(data["results"], many=True, user=request.user).data
return self.success(data)
Empty file added backend/temperature/__init__.py
Empty file.
Empty file added backend/temperature/admin.py
Empty file.
6 changes: 6 additions & 0 deletions backend/temperature/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from django.apps import AppConfig


class TemperatureConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'temperature'
Loading