Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
86 changes: 44 additions & 42 deletions backend/contents/views/oj.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,43 @@
from contest.models import Contest
from problem.models import Problem
from utils.api import APIView
from django.core.cache import cache
from django.utils.timezone import now
import requests
import xml.etree.ElementTree as ET

from utils.constants import RSS_FEED_URL

HOME_STATS_CACHE_KEY = "home_statistics"
HOME_STATS_CACHE_TTL = 60 * 10 # 10분

RSS_CACHE_KEY = "home_rss_feed"
RSS_CACHE_TTL = 60 * 30 # 30분


class GetHomeStatisticsAPI(APIView):

def get(self, request):
"""
총 문제 수, 채점이 완료된 문제 수, 마감된 대회 수를 반환하는 API
"""
problems = Problem.objects.all().filter(visible=True)
cached = cache.get(HOME_STATS_CACHE_KEY)
if cached:
return self.success(cached)

# 총 문제 수
problems = Problem.objects.filter(visible=True)
total_problem_length = problems.count()

# 한번이라도 accept가 된 문제 수
accepted_problem_length = problems.filter(accepted_number__gt=0).count()

# 마감된 대회 수
contests = Contest.objects.select_related("created_by").filter(visible=True)
cur = now()
contests = contests.filter(end_time__gt=cur)
ended_contest_length = contests.count()
ended_contest_length = Contest.objects.filter(visible=True, end_time__gt=cur).count()

home_statistics = {
"total_problem_length": total_problem_length,
"accepted_problem_length": accepted_problem_length,
"ended_contest_length": ended_contest_length,
}

return self.success(HomeStatistics(home_statistics).data)
data = HomeStatistics(home_statistics).data
cache.set(HOME_STATS_CACHE_KEY, data, HOME_STATS_CACHE_TTL)
return self.success(data)


class GetHomeAnnouncementAPI(APIView):
Expand All @@ -55,33 +58,32 @@ def get(self, request):
"""
RSS 공지사항을 JSON으로 파싱하여 반환하는 API
"""

# RSS 피드 가져오기
response = requests.get(RSS_FEED_URL)

if response.status_code == 200:
# XML 파싱
root = ET.fromstring(response.content)

# 필요한 정보 추출
BASE_URL = "https://swedu.pusan.ac.kr"
items = []
for item in root.findall('.//item')[:5]:
link = item.find('link').text or ''
# RSS가 상대 경로(/bbs/...)로 반환할 경우 base URL을 붙여줌
if link and not link.startswith('http'):
link = BASE_URL + link
item_dict = {
'title': item.find('title').text.rstrip("}"),
'link': link,
'pubDate': item.find('pubDate').text
}
items.append(item_dict)

# Serializer를 사용하여 데이터 직렬화
serializer = RSSItemSerializer(items, many=True)

# JsonResponse로 반환
return self.success(serializer.data)
else:
self.error("Failed to fetch RSS feed")
cached = cache.get(RSS_CACHE_KEY)
if cached:
return self.success(cached)

try:
response = requests.get(RSS_FEED_URL, timeout=5)
except requests.RequestException:
return self.error("Failed to fetch RSS feed")

if response.status_code != 200:
return self.error("Failed to fetch RSS feed")

root = ET.fromstring(response.content)
BASE_URL = "https://swedu.pusan.ac.kr"
items = []
for item in root.findall('.//item')[:5]:
link = item.find('link').text or ''
if link and not link.startswith('http'):
link = BASE_URL + link
item_dict = {
'title': item.find('title').text.rstrip("}"),
'link': link,
'pubDate': item.find('pubDate').text
}
items.append(item_dict)

data = RSSItemSerializer(items, many=True).data
cache.set(RSS_CACHE_KEY, data, RSS_CACHE_TTL)
return self.success(data)
7 changes: 7 additions & 0 deletions backend/submission/views/oj.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import ipaddress

from django.db.models import Q, Count
from django.utils.dateparse import parse_datetime

from account.decorators import login_required, check_contest_permission
from utils.testcase_cache import TestCaseCacheManager
Expand Down Expand Up @@ -191,6 +192,12 @@ def get(self, request):
if result:
submissions = submissions.filter(result=result)

since = request.GET.get("since")
if since:
since_dt = parse_datetime(since)
if since_dt:
submissions = submissions.filter(create_time__gte=since_dt)

data = self.paginate_data(request, submissions)
data["results"] = SubmissionListSerializer(data["results"], many=True, user=request.user).data
return self.success(data)
Expand Down
Binary file added frontend/src/assets/code-place-banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added frontend/src/assets/code-place-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
75 changes: 75 additions & 0 deletions frontend/src/assets/images/contest-empty-calendar.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
58 changes: 58 additions & 0 deletions frontend/src/assets/images/contest-empty-clipboard.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion frontend/src/pages/oj/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ h1.main-title {
--title_font-size: 24px;
--title_font-weight: 700;

--point-color: #32306b;
--point-color: #5b64ed;
--pale-point-color: #f8f9ff;
// hex #024D97; to rgb 2, 77, 151
--pnu-green: rgb(6, 186, 110);
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/pages/oj/components/LogoButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<div class="logo" @click="handleRoute('/')">
<div>
<img
src="@/assets/code-place-logo.svg"
src="@/assets/code-place-logo.png"
width="32"
style="vertical-align: middle; margin-right: 10px"
/>
Expand Down Expand Up @@ -49,11 +49,12 @@ export default {
.pnuName {
font-size: 14.5px;
font-weight: normal;
color: #555560;
}
.systemTitle {
font-size: 19px;
font-weight: bold;
color: #32306b;
color: #5b64ed;
}
}
}
Expand Down
Loading