From 36a56712f764dfc4a73db3500fa72ba86d89ea13 Mon Sep 17 00:00:00 2001 From: Jimin Ha Date: Sat, 8 Jan 2022 18:03:15 +0000 Subject: [PATCH 01/39] feat: Add and link user profile page --- frontend/src/pages/oj/components/Header.vue | 6 ++ frontend/src/pages/oj/router/routes.js | 13 +++- frontend/src/pages/oj/views/index.js | 4 +- frontend/src/pages/oj/views/user/Profile.vue | 82 ++++++++++++++++++++ 4 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 frontend/src/pages/oj/views/user/Profile.vue diff --git a/frontend/src/pages/oj/components/Header.vue b/frontend/src/pages/oj/components/Header.vue index b1b6f0252..8ffe70785 100644 --- a/frontend/src/pages/oj/components/Header.vue +++ b/frontend/src/pages/oj/components/Header.vue @@ -28,6 +28,7 @@ @@ -77,6 +78,11 @@ export default { } else { window.open('/admin/') } + }, + async goProfile () { + await this.$router.push({ + name: 'profile' + }) } }, computed: { diff --git a/frontend/src/pages/oj/router/routes.js b/frontend/src/pages/oj/router/routes.js index 762917ab6..fc799d4b2 100644 --- a/frontend/src/pages/oj/router/routes.js +++ b/frontend/src/pages/oj/router/routes.js @@ -22,7 +22,8 @@ import { LectureAssignmentList, LectureAssignmentDetail, LectureQna, - LectureQnaDetail + LectureQnaDetail, + Profile } from '../views' export default [ @@ -123,9 +124,9 @@ export default [ meta: { title: 'Contest Ranking' } }, { - name: 'profile-setting', + name: 'setting', path: '/setting', - meta: { requiresAuth: true, title: 'Profile Settings' }, + meta: { requiresAuth: true, title: 'settings' }, component: ProfileSetting }, { @@ -170,6 +171,12 @@ export default [ meta: { title: 'Lecture QnA Detail' }, component: LectureQnaDetail }, + { + name: 'profile', + path: '/profile', + meta: { requiresAuth: true, title: 'My Profile' }, + component: Profile + }, { path: '*', meta: { title: '404' }, diff --git a/frontend/src/pages/oj/views/index.js b/frontend/src/pages/oj/views/index.js index cc2621340..e6f1f9a77 100644 --- a/frontend/src/pages/oj/views/index.js +++ b/frontend/src/pages/oj/views/index.js @@ -13,6 +13,7 @@ import LectureAssignmentList from './lecture/LectureAssignmentList.vue' import LectureAssignmentDetail from './lecture/LectureAssignmentDetail.vue' import LectureQna from './lecture/LectureQnA.vue' import LectureQnaDetail from './lecture/LectureQnADetail.vue' +import Profile from './user/Profile.vue' // Grouping Components in the Same Chunk const Problem = () => import(/* webpackChunkName: "Problem" */ '@oj/views/problem/Problem.vue') @@ -33,7 +34,8 @@ export { Logout, ProblemList, Announcement, AnnouncementList, Problem, ProblemSet, ApplyResetPassword, ResetPassword, EmailAuth, ProfileSetting, ContestList, ContestDetail, ContestProblemList, ContestRanking, Register, - LectureList, LectureDashboard, LectureAssignmentList, LectureAssignmentDetail, LectureQna, LectureQnaDetail + LectureList, LectureDashboard, LectureAssignmentList, LectureAssignmentDetail, LectureQna, LectureQnaDetail, + Profile } /* 구성 요소 내보내기는 두 가지 범주로 나뉩니다. * 하나는 일반적으로 직접 내보내기에 사용되며 diff --git a/frontend/src/pages/oj/views/user/Profile.vue b/frontend/src/pages/oj/views/user/Profile.vue new file mode 100644 index 000000000..bd1b4cd7c --- /dev/null +++ b/frontend/src/pages/oj/views/user/Profile.vue @@ -0,0 +1,82 @@ + + + + + From d4ade56a3e9704c70d2fb43d46c514c7894a389f Mon Sep 17 00:00:00 2001 From: Jimin Ha Date: Sun, 9 Jan 2022 03:39:21 +0000 Subject: [PATCH 02/39] feat: initialize user profile page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - User profile 페이지의 공통 부분과 탭을 만든다. --- .../oj/components/user/ProfileContest.vue | 38 ++++++++++ .../pages/oj/components/user/ProfileGroup.vue | 40 ++++++++++ .../oj/components/user/ProfileSubmission.vue | 40 ++++++++++ .../oj/components/user/TabSplitInTwo.vue | 60 +++++++++++++++ .../src/pages/oj/components/user/UserInfo.vue | 74 +++++++++++++++++++ frontend/src/pages/oj/views/user/Profile.vue | 14 ++-- 6 files changed, 261 insertions(+), 5 deletions(-) create mode 100644 frontend/src/pages/oj/components/user/ProfileContest.vue create mode 100644 frontend/src/pages/oj/components/user/ProfileGroup.vue create mode 100644 frontend/src/pages/oj/components/user/ProfileSubmission.vue create mode 100644 frontend/src/pages/oj/components/user/TabSplitInTwo.vue create mode 100644 frontend/src/pages/oj/components/user/UserInfo.vue diff --git a/frontend/src/pages/oj/components/user/ProfileContest.vue b/frontend/src/pages/oj/components/user/ProfileContest.vue new file mode 100644 index 000000000..1af2953b0 --- /dev/null +++ b/frontend/src/pages/oj/components/user/ProfileContest.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/frontend/src/pages/oj/components/user/ProfileGroup.vue b/frontend/src/pages/oj/components/user/ProfileGroup.vue new file mode 100644 index 000000000..c1cea032e --- /dev/null +++ b/frontend/src/pages/oj/components/user/ProfileGroup.vue @@ -0,0 +1,40 @@ + + + + + diff --git a/frontend/src/pages/oj/components/user/ProfileSubmission.vue b/frontend/src/pages/oj/components/user/ProfileSubmission.vue new file mode 100644 index 000000000..2e13d44e4 --- /dev/null +++ b/frontend/src/pages/oj/components/user/ProfileSubmission.vue @@ -0,0 +1,40 @@ + + + + + diff --git a/frontend/src/pages/oj/components/user/TabSplitInTwo.vue b/frontend/src/pages/oj/components/user/TabSplitInTwo.vue new file mode 100644 index 000000000..bf34aab5f --- /dev/null +++ b/frontend/src/pages/oj/components/user/TabSplitInTwo.vue @@ -0,0 +1,60 @@ + + + + + diff --git a/frontend/src/pages/oj/components/user/UserInfo.vue b/frontend/src/pages/oj/components/user/UserInfo.vue new file mode 100644 index 000000000..5d7f19cef --- /dev/null +++ b/frontend/src/pages/oj/components/user/UserInfo.vue @@ -0,0 +1,74 @@ + + + + + diff --git a/frontend/src/pages/oj/views/user/Profile.vue b/frontend/src/pages/oj/views/user/Profile.vue index bd1b4cd7c..c91662da9 100644 --- a/frontend/src/pages/oj/views/user/Profile.vue +++ b/frontend/src/pages/oj/views/user/Profile.vue @@ -8,6 +8,7 @@
+ @@ -21,17 +22,20 @@ From 7b0297c5f744e9bbd61690c17aad351da6c491e8 Mon Sep 17 00:00:00 2001 From: DailyLinux Date: Tue, 1 Feb 2022 13:02:25 +0000 Subject: [PATCH 04/39] Change CSS. --- .../pages/oj/components/user/ProfileGroup.vue | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/frontend/src/pages/oj/components/user/ProfileGroup.vue b/frontend/src/pages/oj/components/user/ProfileGroup.vue index 11d6a1a6e..e532ce72d 100644 --- a/frontend/src/pages/oj/components/user/ProfileGroup.vue +++ b/frontend/src/pages/oj/components/user/ProfileGroup.vue @@ -42,13 +42,13 @@

Member

- {{ member }} - - @@ -145,8 +145,8 @@ export default { background-color: #8DC63F !important; border-color: transparent; border: none; - border-radius: 7px; - width: 6%; + border-radius: 10px; + padding: 2px 15px; } .group { display: flex; @@ -167,7 +167,7 @@ export default { display: flex; flex-direction: column; justify-items: center; - margin-left: 20px; + margin-left: 25px; } .group-name{ color: #173747; @@ -180,7 +180,7 @@ export default { margin-top: 5px; } #logo { - width: 89px; + width: 75px; height: 83px; margin-left: 20px; } @@ -224,6 +224,14 @@ export default { font-weight: bold; margin-block: 20px; } + .member { + min-width: 140px; + display: inline-flex; + justify-content: space-between; + align-content: space-between; + margin-right: 20px; + overflow: hidden + } .promote { background-color: #569BD8 !important; border-color: transparent; @@ -256,8 +264,8 @@ export default { background-color: #8DC63F !important; border-color: transparent; border: none; - border-radius: 7px; - width: 7%; + border-radius: 10px; + padding: 2px 15px; margin-inline: 10px; color: #FFFFFF; } @@ -265,8 +273,8 @@ export default { background-color: #FF6663 !important; border-color: transparent; border: none; - border-radius: 7px; - width: 7%; + border-radius: 10px; + padding: 2px 20px; margin-inline: 10px; color: #FFFFFF; } From 438c4534af5726665a534c7968ca89bd172d3e8d Mon Sep 17 00:00:00 2001 From: Jimin Ha Date: Tue, 1 Feb 2022 13:58:31 +0000 Subject: [PATCH 05/39] change section title to common css MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 다른 프로필 탭과의 통일성을 위해 .section title css property 설정 --- .../pages/oj/components/user/ProfileGroup.vue | 21 +++++-------------- frontend/src/styles/common.scss | 2 +- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/frontend/src/pages/oj/components/user/ProfileGroup.vue b/frontend/src/pages/oj/components/user/ProfileGroup.vue index e532ce72d..f54f94b97 100644 --- a/frontend/src/pages/oj/components/user/ProfileGroup.vue +++ b/frontend/src/pages/oj/components/user/ProfileGroup.vue @@ -1,8 +1,8 @@ From 067df10f1cd0d962de38461b5822865731d1bae7 Mon Sep 17 00:00:00 2001 From: goo314 Date: Fri, 28 Jan 2022 13:59:19 +0900 Subject: [PATCH 15/39] chore: Remove chart.js outside frontend folder --- package-lock.json | 24 ------------------------ package.json | 5 ----- 2 files changed, 29 deletions(-) delete mode 100644 package-lock.json delete mode 100644 package.json diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index f156e3265..000000000 --- a/package-lock.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "name": "workspace", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "dependencies": { - "chart.js": "^3.7.0" - } - }, - "node_modules/chart.js": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.7.0.tgz", - "integrity": "sha512-31gVuqqKp3lDIFmzpKIrBeum4OpZsQjSIAqlOpgjosHDJZlULtvwLEZKtEhIAZc7JMPaHlYMys40Qy9Mf+1AAg==" - } - }, - "dependencies": { - "chart.js": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.7.0.tgz", - "integrity": "sha512-31gVuqqKp3lDIFmzpKIrBeum4OpZsQjSIAqlOpgjosHDJZlULtvwLEZKtEhIAZc7JMPaHlYMys40Qy9Mf+1AAg==" - } - } -} diff --git a/package.json b/package.json deleted file mode 100644 index 94c6ea5ef..000000000 --- a/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "dependencies": { - "chart.js": "^3.7.0" - } -} From 668079912b7b375d39d39b3a113ef30b8c3ffaa8 Mon Sep 17 00:00:00 2001 From: goo314 Date: Fri, 28 Jan 2022 18:11:47 +0900 Subject: [PATCH 16/39] fix: Show at most limit pages --- .../src/pages/oj/components/Pagination.vue | 87 ++++++++++++------- 1 file changed, 57 insertions(+), 30 deletions(-) diff --git a/frontend/src/pages/oj/components/Pagination.vue b/frontend/src/pages/oj/components/Pagination.vue index af8714d50..b44bcdb4e 100644 --- a/frontend/src/pages/oj/components/Pagination.vue +++ b/frontend/src/pages/oj/components/Pagination.vue @@ -1,11 +1,17 @@ @@ -14,32 +20,39 @@ export default { name: 'Pagination', props: {}, + data () { + return { + rows: 45, // number of total rows in table + perPage: 3, // number of rows in table per one page + currentPage: 1, + limit: 3 // maximum of number of pages + } + }, methods: { - toFront: function () { - console.log('front') - this.currentPage = 1 - }, - toPrev: function () { - console.log('prev') - if (this.currentPage > 1) { - this.currentPage -= 1 - } - }, - toNext: function () { - console.log('next') - if (this.currentPage < this.limit) { - this.currentPage += 1 + changePage (page) { + if (page >= 1 && page <= this.numberOfPages) { + this.currentPage = page } - }, - toEnd: function () { - console.log('end') - this.currentPage = this.limit } }, - data () { - return { - limit: 5, - currentPage: 3 + computed: { + numberOfPages () { // number of pages (if 9 rows and 3 for each pages, then 3 pages) + return Math.ceil(this.rows / this.perPage) + }, + startPage () { + var start = (Math.trunc((this.currentPage - 1) / this.limit)) * this.limit + 1 + return start + }, + endPage () { + var end = this.startPage + this.limit - 1 + return end < this.numberOfPages ? end : this.numberOfPages + }, + pageList () { + var pages = [] + for (let i = this.startPage; i <= this.endPage; i++) { + pages.push(i) + } + return pages } } } @@ -56,7 +69,7 @@ export default { .page-itm { border-radius: 50rem !important; - padding: 0.25rem; + margin: 20px 5% 16px 0; display: flex; flex-direction: row; } @@ -66,15 +79,29 @@ export default { height: 30px; text-align: center; line-height: 25px; - margin-right: 0.25rem; + margin-left: 0.25rem; border-radius: 5px !important; color: #bdbdbd; border: thin solid #bdbdbd; cursor: pointer; } -.page-btn:hover{ +.page-btn:hover { background-color: #bdbdbd; color: white; } + +.select-page-btn { + width: 30px; + height: 30px; + text-align: center; + line-height: 25px; + margin-left: 0.25rem; + border-radius: 5px !important; + background-color: #bdbdbd; + color: white; + border: thin solid #bdbdbd; + cursor: pointer; +} + From 79c9576a7d9ddd31f4917ad52071bec69672fab9 Mon Sep 17 00:00:00 2001 From: goo314 Date: Fri, 28 Jan 2022 18:53:59 +0900 Subject: [PATCH 17/39] style: Shorten style code --- .../src/pages/oj/components/Pagination.vue | 28 +++++++------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/frontend/src/pages/oj/components/Pagination.vue b/frontend/src/pages/oj/components/Pagination.vue index b44bcdb4e..139b00571 100644 --- a/frontend/src/pages/oj/components/Pagination.vue +++ b/frontend/src/pages/oj/components/Pagination.vue @@ -1,17 +1,17 @@ @@ -86,22 +86,14 @@ export default { cursor: pointer; } -.page-btn:hover { - background-color: #bdbdbd; - color: white; +.page-btn.select { + background-color: #bdbdbd; + color: white; } -.select-page-btn { - width: 30px; - height: 30px; - text-align: center; - line-height: 25px; - margin-left: 0.25rem; - border-radius: 5px !important; +.page-btn:hover { background-color: #bdbdbd; color: white; - border: thin solid #bdbdbd; - cursor: pointer; } From c7fcc3b64595c7d09773b7bad4b4f960421ae0ae Mon Sep 17 00:00:00 2001 From: goo314 Date: Sat, 29 Jan 2022 01:58:21 +0900 Subject: [PATCH 18/39] fix: Add component communication(props, event) --- .../src/pages/oj/components/Pagination.vue | 89 ++++++++++--------- 1 file changed, 49 insertions(+), 40 deletions(-) diff --git a/frontend/src/pages/oj/components/Pagination.vue b/frontend/src/pages/oj/components/Pagination.vue index 139b00571..f3c95b3f0 100644 --- a/frontend/src/pages/oj/components/Pagination.vue +++ b/frontend/src/pages/oj/components/Pagination.vue @@ -1,51 +1,57 @@ From 7b4ac7d9978e18e580aa942ac72e8b6e3bc9f37c Mon Sep 17 00:00:00 2001 From: goo314 Date: Sat, 29 Jan 2022 01:59:17 +0900 Subject: [PATCH 19/39] style: Apply Pagination vue --- .../pages/oj/components/user/ProfileContest.vue | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/frontend/src/pages/oj/components/user/ProfileContest.vue b/frontend/src/pages/oj/components/user/ProfileContest.vue index f62d88493..a7ed0300b 100644 --- a/frontend/src/pages/oj/components/user/ProfileContest.vue +++ b/frontend/src/pages/oj/components/user/ProfileContest.vue @@ -29,14 +29,13 @@
-
@@ -51,6 +50,9 @@ export default { props: {}, data() { return { + rows: 100, + currentPage: 1, + perPage: 3, Chart, fields: [ { key: "date", label: "Date" }, From 77eb69b4de05b04f9c31969f170f6780bd6cb5f8 Mon Sep 17 00:00:00 2001 From: goo314 Date: Sun, 6 Feb 2022 21:31:44 +0900 Subject: [PATCH 20/39] style: Edit code style --- .../src/pages/oj/components/Pagination.vue | 22 +++++++++---------- frontend/src/pages/oj/router/routes.js | 2 +- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/frontend/src/pages/oj/components/Pagination.vue b/frontend/src/pages/oj/components/Pagination.vue index f3c95b3f0..3475c767a 100644 --- a/frontend/src/pages/oj/components/Pagination.vue +++ b/frontend/src/pages/oj/components/Pagination.vue @@ -42,23 +42,20 @@ export default { } }, computed: { + localLimit () { + return Number(this.limit) + }, numberOfPages () { // number of pages return Math.ceil(this.totalRows / this.perPage) }, startPage () { - var start = (Math.trunc((this.currentPage - 1) / this.limit)) * this.limit + 1 - return start + return Math.trunc((this.currentPage - 1) / this.localLimit) * this.localLimit + 1 }, endPage () { - var end = this.startPage + Number(this.limit) - 1 - return end <= this.numberOfPages ? end : this.numberOfPages + return this.startPage + this.localLimit - 1 <= this.numberOfPages ? this.startPage + this.localLimit - 1 : this.numberOfPages }, pageList () { - var pages = [] - for (let i = this.startPage; i <= this.endPage; i++) { - pages.push(i) - } - return pages + return [...Array(this.endPage - this.startPage + 1).keys()].map(i => i + this.startPage) } } } @@ -84,21 +81,22 @@ export default { cursor: pointer; } -.page-btn.leftedge { +.leftedge { border-top-left-radius: 0.25rem !important; border-bottom-left-radius: 0.25rem !important; } -.page-btn.rightedge { +.rightedge { border-top-right-radius: 0.25rem !important; border-bottom-right-radius: 0.25rem !important; } -.page-btn.select { +.select { background-color: #bdbdbd; border: thin solid #bdbdbd; color: white; pointer-events: none; + z-index: 1; } .page-btn:hover { diff --git a/frontend/src/pages/oj/router/routes.js b/frontend/src/pages/oj/router/routes.js index 1fba9c217..01e2cf316 100644 --- a/frontend/src/pages/oj/router/routes.js +++ b/frontend/src/pages/oj/router/routes.js @@ -175,7 +175,7 @@ export default [ { name: 'profile', path: '/profile', - meta: { title: 'My Profile' }, + meta: { requiresAuth: true, title: 'My Profile' }, component: Profile }, { From 90e2509d625d1fa59a3b1b03df530d4576dbe784 Mon Sep 17 00:00:00 2001 From: goo314 Date: Tue, 8 Feb 2022 12:23:05 +0000 Subject: [PATCH 21/39] feat: Create USerContestAPI --- backend/contest/serializers.py | 5 +++++ backend/contest/urls/oj.py | 2 ++ backend/contest/views/oj.py | 17 +++++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/backend/contest/serializers.py b/backend/contest/serializers.py index 8e52ab21a..ab44f0a5e 100644 --- a/backend/contest/serializers.py +++ b/backend/contest/serializers.py @@ -174,3 +174,8 @@ class ACMContesHelperSerializer(serializers.Serializer): problem_id = serializers.CharField() rank_id = serializers.IntegerField() checked = serializers.BooleanField() + +class UserContestSerializer(serializers.Serializer): + id = serializers.IntegerField() + title = serializers.CharField(max_length=128) + start_time = serializers.DateTimeField() diff --git a/backend/contest/urls/oj.py b/backend/contest/urls/oj.py index 398f0ab68..0a5ddcbfe 100644 --- a/backend/contest/urls/oj.py +++ b/backend/contest/urls/oj.py @@ -3,6 +3,7 @@ from ..views.oj import ContestAnnouncementListAPI from ..views.oj import ContestPasswordVerifyAPI, ContestAccessAPI from ..views.oj import ContestListAPI, ContestAPI, ContestRankAPI, ProblemBankAPI +from ..views.oj import UserContestAPI urlpatterns = [ path("contests/", ContestListAPI.as_view(), name="contest_list_api"), @@ -12,4 +13,5 @@ path("contest/access/", ContestAccessAPI.as_view(), name="contest_access_api"), path("contest/rank/", ContestRankAPI.as_view(), name="contest_rank_api"), path("contest/bank/", ProblemBankAPI.as_view(), name="contest_bank_api"), + path("contest/user/", UserContestAPI.as_view(), name="contest_user_api"), ] diff --git a/backend/contest/views/oj.py b/backend/contest/views/oj.py index a2c3b7661..f49f3f037 100644 --- a/backend/contest/views/oj.py +++ b/backend/contest/views/oj.py @@ -16,6 +16,7 @@ from ..serializers import ACMContestRankNoPenaltySerializer, ContestAnnouncementSerializer from ..serializers import ContestSerializer, ContestPasswordVerifySerializer from ..serializers import ACMContestRankSerializer +from ..serializers import UserContestSerializer import random import json @@ -277,3 +278,19 @@ def get(self, request): except ProblemBank.DoesNotExist: return self.success(False) return self.success(True) + + +class UserContestAPI(APIView): + def get(self, request): + user = request.user + qs = ACMContestRank.objects.filter(user = user.id, user__admin_type=AdminType.REGULAR_USER, user__is_disabled=False).\ + select_related("user").order_by("-accepted_number", "total_penalty", "total_time") + contest_ids = [ dic['contest'] for dic in ACMContestRankSerializer(qs, many=True).data ] + contests = [] + for contest_id in contest_ids: + try: + contest = Contest.objects.get(id=contest_id, visible=True) + contests.append(contest) + except Contest.DoesNotExist: + return self.error("Contest does not exist") + return self.success(UserContestSerializer(contests, many=True).data) From fb4d8bd6464a5f6ec1f6c8d6def3a9506bdf4122 Mon Sep 17 00:00:00 2001 From: goo314 Date: Wed, 9 Feb 2022 06:30:59 +0000 Subject: [PATCH 22/39] add: Calate and return user rank in UserContestAPI --- backend/contest/serializers.py | 2 ++ backend/contest/views/oj.py | 20 +++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/backend/contest/serializers.py b/backend/contest/serializers.py index ab44f0a5e..053bc7ac5 100644 --- a/backend/contest/serializers.py +++ b/backend/contest/serializers.py @@ -179,3 +179,5 @@ class UserContestSerializer(serializers.Serializer): id = serializers.IntegerField() title = serializers.CharField(max_length=128) start_time = serializers.DateTimeField() + rank = serializers.IntegerField() + percentage = serializers.FloatField() diff --git a/backend/contest/views/oj.py b/backend/contest/views/oj.py index f49f3f037..ce263f168 100644 --- a/backend/contest/views/oj.py +++ b/backend/contest/views/oj.py @@ -281,15 +281,29 @@ def get(self, request): class UserContestAPI(APIView): + def get(self, request): user = request.user - qs = ACMContestRank.objects.filter(user = user.id, user__admin_type=AdminType.REGULAR_USER, user__is_disabled=False).\ - select_related("user").order_by("-accepted_number", "total_penalty", "total_time") - contest_ids = [ dic['contest'] for dic in ACMContestRankSerializer(qs, many=True).data ] + # 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) + # list for contest id which user participated + contest_ids = [ dic['contest'] for dic in ACMContestRankSerializer(qs_problems, many=True).data ] + # contest object information about each contest id contests = [] for contest_id in contest_ids: try: contest = Contest.objects.get(id=contest_id, visible=True) + contest = ContestSerializer(contest).data + # to calculate rank + qs_participants = ACMContestRank.objects.filter(contest=contest_id, user__admin_type=AdminType.REGULAR_USER, user__is_disabled=False).\ + select_related("user").order_by("-accepted_number", "total_penalty", "total_time") + participants = ACMContestRankSerializer(qs_participants, many=True).data + total_participants = len(participants) + for i in range(total_participants): + if participants[i]['user']['id'] == user.id: + contest['rank'] = i+1 + contest['percentage'] = contest['rank']/(total_participants)*100 + break contests.append(contest) except Contest.DoesNotExist: return self.error("Contest does not exist") From 1725bc79c88abb57c85c91326b89f4ab14d692c5 Mon Sep 17 00:00:00 2001 From: goo314 Date: Thu, 10 Feb 2022 04:27:27 +0000 Subject: [PATCH 23/39] style: Change code style --- backend/contest/serializers.py | 1 + backend/contest/views/oj.py | 11 +++++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/contest/serializers.py b/backend/contest/serializers.py index 053bc7ac5..56f1375cd 100644 --- a/backend/contest/serializers.py +++ b/backend/contest/serializers.py @@ -175,6 +175,7 @@ class ACMContesHelperSerializer(serializers.Serializer): rank_id = serializers.IntegerField() checked = serializers.BooleanField() + class UserContestSerializer(serializers.Serializer): id = serializers.IntegerField() title = serializers.CharField(max_length=128) diff --git a/backend/contest/views/oj.py b/backend/contest/views/oj.py index ce263f168..151328cf3 100644 --- a/backend/contest/views/oj.py +++ b/backend/contest/views/oj.py @@ -281,13 +281,12 @@ def get(self, request): 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) + qs_problems = ACMContestRank.objects.filter(user=user.id, user__admin_type=AdminType.REGULAR_USER, user__is_disabled=False) # list for contest id which user participated - contest_ids = [ dic['contest'] for dic in ACMContestRankSerializer(qs_problems, many=True).data ] + contest_ids = [dic["contest"] for dic in ACMContestRankSerializer(qs_problems, many=True).data] # contest object information about each contest id contests = [] for contest_id in contest_ids: @@ -300,9 +299,9 @@ def get(self, request): participants = ACMContestRankSerializer(qs_participants, many=True).data total_participants = len(participants) for i in range(total_participants): - if participants[i]['user']['id'] == user.id: - contest['rank'] = i+1 - contest['percentage'] = contest['rank']/(total_participants)*100 + if participants[i]["user"]["id"] == user.id: + contest["rank"] = i+1 + contest["percentage"] = contest["rank"]/(total_participants)*100 break contests.append(contest) except Contest.DoesNotExist: From 883bd9faf0aceeef0ac9dcca78db95638f9eabd3 Mon Sep 17 00:00:00 2001 From: goo314 Date: Thu, 10 Feb 2022 07:09:37 +0000 Subject: [PATCH 24/39] add: Add rank field in ACMContestRank & Shorten UserContestAPI --- backend/contest/models.py | 13 +++++++++++++ backend/contest/views/oj.py | 19 +++++-------------- 2 files changed, 18 insertions(+), 14 deletions(-) diff --git a/backend/contest/models.py b/backend/contest/models.py index b160120c9..545f47183 100644 --- a/backend/contest/models.py +++ b/backend/contest/models.py @@ -8,6 +8,8 @@ from group.models import Group from utils.models import RichTextField +from account.models import AdminType + class Contest(models.Model): title = models.TextField() @@ -92,6 +94,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"),) diff --git a/backend/contest/views/oj.py b/backend/contest/views/oj.py index 151328cf3..2112cad7d 100644 --- a/backend/contest/views/oj.py +++ b/backend/contest/views/oj.py @@ -285,24 +285,15 @@ 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) - # list for contest id which user participated - contest_ids = [dic["contest"] for dic in ACMContestRankSerializer(qs_problems, many=True).data] - # contest object information about each contest id contests = [] - for contest_id in contest_ids: + for problem in qs_problems: + contest_id = problem.contest.id try: contest = Contest.objects.get(id=contest_id, visible=True) contest = ContestSerializer(contest).data - # to calculate rank - qs_participants = ACMContestRank.objects.filter(contest=contest_id, user__admin_type=AdminType.REGULAR_USER, user__is_disabled=False).\ - select_related("user").order_by("-accepted_number", "total_penalty", "total_time") - participants = ACMContestRankSerializer(qs_participants, many=True).data - total_participants = len(participants) - for i in range(total_participants): - if participants[i]["user"]["id"] == user.id: - contest["rank"] = i+1 - contest["percentage"] = contest["rank"]/(total_participants)*100 - break + 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") From af494ed9fb936e0c3ce8b98954a93262c02239fa Mon Sep 17 00:00:00 2001 From: goo314 Date: Mon, 14 Feb 2022 23:47:33 +0900 Subject: [PATCH 25/39] add: Connect UserContestAPI to ProfileContest page --- frontend/src/pages/oj/api.js | 3 + .../oj/components/user/ProfileContest.vue | 89 ++++++++++--------- 2 files changed, 51 insertions(+), 41 deletions(-) diff --git a/frontend/src/pages/oj/api.js b/frontend/src/pages/oj/api.js index 88c1a1fcd..45bee1a79 100644 --- a/frontend/src/pages/oj/api.js +++ b/frontend/src/pages/oj/api.js @@ -208,6 +208,9 @@ export default { } }) }, + getUserContestInfo () { + return ajax('contest/user/', 'get') + }, submitCode (data) { return ajax('submission/', 'post', { data diff --git a/frontend/src/pages/oj/components/user/ProfileContest.vue b/frontend/src/pages/oj/components/user/ProfileContest.vue index a7ed0300b..a1194ef69 100644 --- a/frontend/src/pages/oj/components/user/ProfileContest.vue +++ b/frontend/src/pages/oj/components/user/ProfileContest.vue @@ -16,16 +16,24 @@
- - - @@ -136,6 +149,7 @@ export default { flex-direction: column; align-items: center; } + .rank-chart { width: 90%; } @@ -152,6 +166,11 @@ export default { margin: 0 auto; cursor: pointer; } + +.underwayClass { + backgroundColor: red; +} + .pagination { width: 95%; margin-right: 5%; From f3246cc23e24de9671e66ddd0374bc0da6ff089f Mon Sep 17 00:00:00 2001 From: goo314 Date: Sat, 26 Feb 2022 12:26:09 +0900 Subject: [PATCH 28/39] feat: Add test for user-contest-api --- ...bcb (feat: Add test for user-contest-api) | Bin 0 -> 8196 bytes ...bcb (feat: Add test for user-contest-api) | Bin 0 -> 10244 bytes backend/contest/tests.py | 64 +++++++++++++++++- 3 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 .DS_Store~1787bcb (feat: Add test for user-contest-api) create mode 100644 backend/.DS_Store~1787bcb (feat: Add test for user-contest-api) diff --git a/.DS_Store~1787bcb (feat: Add test for user-contest-api) b/.DS_Store~1787bcb (feat: Add test for user-contest-api) new file mode 100644 index 0000000000000000000000000000000000000000..7c927092288a2f87bb116e6afc60ad78d46701f6 GIT binary patch literal 8196 zcmeHLziSjh6ntGlC#>NjuZ?%rl^PfZ~K-@wdw%zSU=tdqYHKwj0e5O%YKQ z)#Co$Xhsw}Zfl|J@I3+0AfBj6DYa;suF$mtTRYSNbwC|Z2h;&|U^_T~Z?-MflJCA7 z)m9x)2mVV3?D-I&S`0mA4*BRn!zlo865U+z8+ic71Rg_=nL`f6oGyDXKGgUW!?mG)w^qNpL|_^b>?>w(O*U^ElNc%Fg;F?8ZF?)VOgp*z2_I@ znAEoV^Te0apIF-dSLZGSUXaAM#5+L2KlspDh3a{kR{)=`*C>KVtAx(?UwSY3Ot zD=EMEmnh+1U5c?+9qA7pxOZ+GSL!_Wx_9dK2J0-HTk8E2zSK>JZtgy9eF7oPF~trcXkRm1)3F5Yxg9L zP!6v$U%wYCIyg44*c?$TXENDBISWtQiM7Y4av$Fu4~xogBC2vx?G{+HWDVU=C-{`` zGJJA0N<|#O&Vr4l`My8Ja*k)`x<7n*t2svYT`bqxZYEyvZc!ppJ9tmwHBMt-8_K!w zQ&b%1dw=&MU(?V&IoH3%3ZA5Swo=wAJW7*hx5n3W=S}bve$MMkF5uM=Q`askCWJ>^ z*IbD%2oB!iOW+XrIR3_wC%!JsU&jvf3)~obR`srG9rc8&n)3IntO6~A=b9rphiu2s z^AA>Sy^DVMtVBl%K8ZQSr`6tjlVCF{w4gKB8~vQi!Wx?obN}i-IFqOt!Gpdj9&yT} zE~aTjau}cIK91jWb+v0@Nox`0a_lg_z>VAS=P?r%DqvPFOYB{@_4t{e*JGGp;-=6wCGfnM)K9yLL_Ymq zdO3FY$whzcET4}&JVN5@c~8&;p8R>RXsEvR?;1bwa{OO60_|Bo#}AJX_}(x8@Jp~v z6LRFG;5x^omjR~E;b@IMiBnVkC4o~xeUHPI8fW>|tL6xfoup%j9-3Nj=!dh2juL## zkJlvFsCmz#^6?!f6o?(Tqxpyk&gbs6Xa4**^uzf?M+rWyx(?d${HcOX5ek{_G3X>% zc}G=6hQ@Jjd+sgy+#+J@_sHL31<%+r;BmiH^(+<|=CJ4UhV*hyUvtlWPAs2m6&@k* z_YEujIL%{|BP~|KAEI*N0QUDX>iy@Zy=FOh3rkz4a!VYPpiaF^oes6<0G>r{JVlG_ZO* r9v{?p{9YU?_Moz=;eFcLnz6Wo_Rs$ez&?ZceeKr&ZvFqCt^a=mxNH?_ literal 0 HcmV?d00001 diff --git a/backend/contest/tests.py b/backend/contest/tests.py index 8655e4783..5d4aa6b0a 100644 --- a/backend/contest/tests.py +++ b/backend/contest/tests.py @@ -6,7 +6,9 @@ from utils.api.tests import APITestCase -from .models import ContestAnnouncement, ContestRuleType, Contest +from .models import ContestAnnouncement, ContestRuleType, Contest, ACMContestRank +from submission.models import Submission +from problem.models import Problem, ProblemIOMode from problem.models import ProblemIOMode @@ -39,6 +41,34 @@ "bank_filter": [], "visible": True, "real_time_rank": True, "rank_penalty_visible": True} +DEFAULT_PROBLEM_DATA = {"_id": "A-110", "title": "test", "description": "

test

", "input_description": "test", + "output_description": "test", "time_limit": 1000, "memory_limit": 256, "difficulty": "Level1", + "visible": True, "languages": ["C", "C++", "Java", "Python2"], "template": {}, + "samples": [{"input": "test", "output": "test"}], "spj": False, "spj_language": "C", + "spj_code": "", "spj_compile_ok": True, "test_case_id": "499b26290cc7994e0b497212e842ea85", + "test_case_score": [{"output_name": "1.out", "input_name": "1.in", "output_size": 0, + "stripped_output_md5": "d41d8cd98f00b204e9800998ecf8427e", + "input_size": 0, "score": 0}], + "io_mode": {"io_mode": ProblemIOMode.standard, "input": "input.txt", "output": "output.txt"}, + "share_submission": False, + "rule_type": "ACM", "hint": "

test

", "source": "test"} + +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): @@ -251,4 +281,36 @@ def test_create_problem_bank(self): self.create_user("2018123123", "123123") url = self.reverse("contest_bank_api") response = self.client.post(url, data={"contest_id": contest["id"]}) + + +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 + self.problem = Problem.objects.create(created_by=admin, **data) + + # 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) From 4d0313c62c6bb37d28b5f9f2be5bb97689f27731 Mon Sep 17 00:00:00 2001 From: Jimin Ha Date: Fri, 25 Feb 2022 03:23:10 +0000 Subject: [PATCH 29/39] feat: add contestprize model --- backend/contest/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/contest/models.py b/backend/contest/models.py index 545f47183..258bf2a8a 100644 --- a/backend/contest/models.py +++ b/backend/contest/models.py @@ -17,6 +17,7 @@ class Contest(models.Model): requirements = JSONField(default=list) constraints = JSONField(default=list) allowed_groups = models.ManyToManyField(Group, blank=True) + # 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() From 285db50667a4d5f3c71835aecb329fd9aaa28268 Mon Sep 17 00:00:00 2001 From: goo314 Date: Tue, 1 Mar 2022 13:54:54 +0000 Subject: [PATCH 30/39] style: Change code style --- backend/contest/tests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/contest/tests.py b/backend/contest/tests.py index 5d4aa6b0a..7b2010a6b 100644 --- a/backend/contest/tests.py +++ b/backend/contest/tests.py @@ -66,8 +66,8 @@ 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} + "submission_info": {"1": {"is_ac": True, "ac_time": 123, "penalty": 123, "problem_submission": 1}}, + "contest": 1} class ContestAdminAPITest(APITestCase): From 4f7df7019215f17d68e008af26a14d07fb632712 Mon Sep 17 00:00:00 2001 From: Jimin Ha Date: Wed, 2 Mar 2022 05:32:34 +0000 Subject: [PATCH 31/39] migrate model change --- .../migrations/0013_auto_20220302_1413.py | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 backend/contest/migrations/0013_auto_20220302_1413.py diff --git a/backend/contest/migrations/0013_auto_20220302_1413.py b/backend/contest/migrations/0013_auto_20220302_1413.py new file mode 100644 index 000000000..5727aedff --- /dev/null +++ b/backend/contest/migrations/0013_auto_20220302_1413.py @@ -0,0 +1,49 @@ +# Generated by Django 3.2.12 on 2022-03-02 05:13 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('contest', '0012_rename_total_score_acmcontestrank_total_penalty'), + ] + + 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'), + ), + ] From 08d23a9a474b2344f7a5f06880185a3a4c552997 Mon Sep 17 00:00:00 2001 From: Jimin Ha Date: Wed, 2 Mar 2022 06:37:37 +0000 Subject: [PATCH 32/39] resolve conflict: whiel rebase on master --- ...0302_1413.py => 0014_auto_20220302_1508.py} | 4 ++-- .../migrations/0015_submission_title.py | 18 ------------------ ...de_length.py => 0017_auto_20220302_1508.py} | 9 +++++++-- backend/submission/urls.py | 2 +- frontend/src/pages/oj/router/routes.js | 3 +-- frontend/src/pages/oj/views/index.js | 2 +- 6 files changed, 12 insertions(+), 26 deletions(-) rename backend/contest/migrations/{0013_auto_20220302_1413.py => 0014_auto_20220302_1508.py} (92%) delete mode 100644 backend/submission/migrations/0015_submission_title.py rename backend/submission/migrations/{0014_submission_code_length.py => 0017_auto_20220302_1508.py} (54%) diff --git a/backend/contest/migrations/0013_auto_20220302_1413.py b/backend/contest/migrations/0014_auto_20220302_1508.py similarity index 92% rename from backend/contest/migrations/0013_auto_20220302_1413.py rename to backend/contest/migrations/0014_auto_20220302_1508.py index 5727aedff..e77580b74 100644 --- a/backend/contest/migrations/0013_auto_20220302_1413.py +++ b/backend/contest/migrations/0014_auto_20220302_1508.py @@ -1,4 +1,4 @@ -# Generated by Django 3.2.12 on 2022-03-02 05:13 +# Generated by Django 3.2.12 on 2022-03-02 06:08 from django.db import migrations, models import django.db.models.deletion @@ -7,7 +7,7 @@ class Migration(migrations.Migration): dependencies = [ - ('contest', '0012_rename_total_score_acmcontestrank_total_penalty'), + ('contest', '0013_contestannouncement_problem'), ] operations = [ diff --git a/backend/submission/migrations/0015_submission_title.py b/backend/submission/migrations/0015_submission_title.py deleted file mode 100644 index d73d95187..000000000 --- a/backend/submission/migrations/0015_submission_title.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 3.2.12 on 2022-02-11 07:18 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('submission', '0014_submission_code_length'), - ] - - operations = [ - migrations.AddField( - model_name='submission', - name='title', - field=models.TextField(null=True), - ), - ] diff --git a/backend/submission/migrations/0014_submission_code_length.py b/backend/submission/migrations/0017_auto_20220302_1508.py similarity index 54% rename from backend/submission/migrations/0014_submission_code_length.py rename to backend/submission/migrations/0017_auto_20220302_1508.py index 5a0cc7eef..a4a912204 100644 --- a/backend/submission/migrations/0014_submission_code_length.py +++ b/backend/submission/migrations/0017_auto_20220302_1508.py @@ -1,4 +1,4 @@ -# Generated by Django 3.2.12 on 2022-02-10 10:37 +# Generated by Django 3.2.12 on 2022-03-02 06:08 from django.db import migrations, models @@ -6,7 +6,7 @@ class Migration(migrations.Migration): dependencies = [ - ('submission', '0013_auto_20211030_1535'), + ('submission', '0016_auto_20211226_1458'), ] operations = [ @@ -15,4 +15,9 @@ class Migration(migrations.Migration): name='code_length', field=models.IntegerField(default=0), ), + migrations.AddField( + model_name='submission', + name='title', + field=models.TextField(null=True), + ), ] diff --git a/backend/submission/urls.py b/backend/submission/urls.py index 6527232df..dd88cac4a 100644 --- a/backend/submission/urls.py +++ b/backend/submission/urls.py @@ -2,7 +2,7 @@ from .views import (SubmissionAPI, SubmissionListAPI, ContestSubmissionListAPI, AssignmentSubmissionListAPI, AssignmentSubmissionListProfessorAPI, SubmissionExistsAPI, EditSubmissionScoreAPI) -from .views import ProfileSubmissionListAPI, SubmissionAPI, SubmissionListAPI, ContestSubmissionListAPI, SubmissionExistsAPI +from .views import ProfileSubmissionListAPI urlpatterns = [ path("submission/", SubmissionAPI.as_view(), name="submission_api"), diff --git a/frontend/src/pages/oj/router/routes.js b/frontend/src/pages/oj/router/routes.js index 01e2cf316..fc799d4b2 100644 --- a/frontend/src/pages/oj/router/routes.js +++ b/frontend/src/pages/oj/router/routes.js @@ -23,8 +23,7 @@ import { LectureAssignmentDetail, LectureQna, LectureQnaDetail, - Profile, - ProfileContest + Profile } from '../views' export default [ diff --git a/frontend/src/pages/oj/views/index.js b/frontend/src/pages/oj/views/index.js index ef13e9978..e6f1f9a77 100644 --- a/frontend/src/pages/oj/views/index.js +++ b/frontend/src/pages/oj/views/index.js @@ -35,7 +35,7 @@ export { ApplyResetPassword, ResetPassword, EmailAuth, ProfileSetting, ContestList, ContestDetail, ContestProblemList, ContestRanking, Register, LectureList, LectureDashboard, LectureAssignmentList, LectureAssignmentDetail, LectureQna, LectureQnaDetail, - Profile, ProfileContest + Profile } /* 구성 요소 내보내기는 두 가지 범주로 나뉩니다. * 하나는 일반적으로 직접 내보내기에 사용되며 From 22fe0eedf170cf6dbd2df88acce3ca2e804a474f Mon Sep 17 00:00:00 2001 From: Jimin Ha Date: Wed, 2 Mar 2022 06:38:04 +0000 Subject: [PATCH 33/39] Fix UserContestAPITest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Problem Tag을 제대로 사용하여 테스트 코드를 작성한다. --- backend/contest/tests.py | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/backend/contest/tests.py b/backend/contest/tests.py index 7b2010a6b..20b8eabb4 100644 --- a/backend/contest/tests.py +++ b/backend/contest/tests.py @@ -8,9 +8,7 @@ from .models import ContestAnnouncement, ContestRuleType, Contest, ACMContestRank from submission.models import Submission -from problem.models import Problem, ProblemIOMode - -from problem.models import ProblemIOMode +from problem.models import Problem, ProblemIOMode, ProblemTag DEFAULT_PROBLEM_DATA = {"_id": "A-110", "title": "test", "description": "

test

", "input_description": "test", "output_description": "test", "time_limit": 1000, "memory_limit": 256, "difficulty": "Level1", @@ -41,18 +39,6 @@ "bank_filter": [], "visible": True, "real_time_rank": True, "rank_penalty_visible": True} -DEFAULT_PROBLEM_DATA = {"_id": "A-110", "title": "test", "description": "

test

", "input_description": "test", - "output_description": "test", "time_limit": 1000, "memory_limit": 256, "difficulty": "Level1", - "visible": True, "languages": ["C", "C++", "Java", "Python2"], "template": {}, - "samples": [{"input": "test", "output": "test"}], "spj": False, "spj_language": "C", - "spj_code": "", "spj_compile_ok": True, "test_case_id": "499b26290cc7994e0b497212e842ea85", - "test_case_score": [{"output_name": "1.out", "input_name": "1.in", "output_size": 0, - "stripped_output_md5": "d41d8cd98f00b204e9800998ecf8427e", - "input_size": 0, "score": 0}], - "io_mode": {"io_mode": ProblemIOMode.standard, "input": "input.txt", "output": "output.txt"}, - "share_submission": False, - "rule_type": "ACM", "hint": "

test

", "source": "test"} - DEFAULT_SUBMISSION_DATA = { "problem_id": "1", "user_id": 1, @@ -292,8 +278,17 @@ def setUp(self): # create problem in contest data = copy.deepcopy(DEFAULT_PROBLEM_DATA) data["contest_id"] = self.contest.id - self.problem = Problem.objects.create(created_by=admin, **data) + 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) From 564e9bacd2b5b30882e2c7b9374f1739042e8d16 Mon Sep 17 00:00:00 2001 From: Jimin Ha Date: Wed, 2 Mar 2022 06:47:02 +0000 Subject: [PATCH 34/39] minor design change followed by tailwind css MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - tailwind css로 도입으로 깨진 디자인들을 복구한다. --- frontend/src/pages/oj/components/user/ProfileSubmission.vue | 1 + frontend/src/pages/oj/components/user/TabSplitInTwo.vue | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/pages/oj/components/user/ProfileSubmission.vue b/frontend/src/pages/oj/components/user/ProfileSubmission.vue index 813a06eb5..1038a6eaf 100644 --- a/frontend/src/pages/oj/components/user/ProfileSubmission.vue +++ b/frontend/src/pages/oj/components/user/ProfileSubmission.vue @@ -270,6 +270,7 @@ export default { display:inline; position:relative; top:36px; + font-size: 2rem; } .submission-list-card{ margin:0 auto; diff --git a/frontend/src/pages/oj/components/user/TabSplitInTwo.vue b/frontend/src/pages/oj/components/user/TabSplitInTwo.vue index bf34aab5f..f24a6efc8 100644 --- a/frontend/src/pages/oj/components/user/TabSplitInTwo.vue +++ b/frontend/src/pages/oj/components/user/TabSplitInTwo.vue @@ -44,7 +44,7 @@ export default { .tab-split-in-two { display: flex; justify-content: space-between; - margin: 0px 50px; + margin: 10px 50px; overflow: auto; .tab-split-in-two__tab { margin: 0px 10px; From d993ad92c365f84a1adede1043d866b5914c8190 Mon Sep 17 00:00:00 2001 From: Jimin Ha Date: Fri, 1 Apr 2022 15:19:13 +0000 Subject: [PATCH 35/39] resolve conflict --- .../migrations/0014_auto_20220302_1508.py | 49 ------------------- 1 file changed, 49 deletions(-) delete mode 100644 backend/contest/migrations/0014_auto_20220302_1508.py diff --git a/backend/contest/migrations/0014_auto_20220302_1508.py b/backend/contest/migrations/0014_auto_20220302_1508.py deleted file mode 100644 index e77580b74..000000000 --- a/backend/contest/migrations/0014_auto_20220302_1508.py +++ /dev/null @@ -1,49 +0,0 @@ -# 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'), - ), - ] From 86c294be09bbd64fdc86e7d0de742ec7ec0dc23e Mon Sep 17 00:00:00 2001 From: Kohminchae Date: Wed, 23 Mar 2022 05:09:37 +0900 Subject: [PATCH 36/39] move user setting --- .../user/UserSetting.vue} | 4 ++-- frontend/src/pages/oj/router/routes.js | 7 ------- frontend/src/pages/oj/views/user/Profile.vue | 8 ++++++-- 3 files changed, 8 insertions(+), 11 deletions(-) rename frontend/src/pages/oj/{views/user/ProfileSetting.vue => components/user/UserSetting.vue} (99%) diff --git a/frontend/src/pages/oj/views/user/ProfileSetting.vue b/frontend/src/pages/oj/components/user/UserSetting.vue similarity index 99% rename from frontend/src/pages/oj/views/user/ProfileSetting.vue rename to frontend/src/pages/oj/components/user/UserSetting.vue index dcc5bd8af..7a4be338b 100644 --- a/frontend/src/pages/oj/views/user/ProfileSetting.vue +++ b/frontend/src/pages/oj/components/user/UserSetting.vue @@ -1,5 +1,5 @@ @@ -27,6 +28,7 @@ import TabSplitInTwo from '../../components/user/TabSplitInTwo.vue' import ProfileSubmission from '../../components/user/ProfileSubmission.vue' import ProfileContest from '../../components/user/ProfileContest.vue' import ProfileGroup from '../../components/user/ProfileGroup.vue' +import UserSetting from '../../components/user/UserSetting.vue' export default { name: 'ProfileSetting', @@ -35,7 +37,8 @@ export default { TabSplitInTwo, ProfileSubmission, ProfileContest, - ProfileGroup + ProfileGroup, + UserSetting }, data () { return { @@ -53,7 +56,8 @@ export default { 'Submission', 'History', 'Contest', - 'Badge' + 'Badge', + 'Setting' ], rightTabs: [ 'Storage', From 59b24dd9319fe7779408a39c42ea655074c52a56 Mon Sep 17 00:00:00 2001 From: Kohminchae Date: Wed, 23 Mar 2022 09:07:57 +0000 Subject: [PATCH 37/39] change details in user setting --- frontend/src/pages/oj/components/Header.vue | 3 +-- frontend/src/pages/oj/components/user/UserSetting.vue | 5 ++--- frontend/src/pages/oj/views/index.js | 5 +++-- frontend/src/pages/oj/views/problem/Problem.vue | 7 ++++--- frontend/src/pages/oj/views/user/Profile.vue | 8 ++++---- 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/frontend/src/pages/oj/components/Header.vue b/frontend/src/pages/oj/components/Header.vue index 8ffe70785..6c61a6f09 100644 --- a/frontend/src/pages/oj/components/Header.vue +++ b/frontend/src/pages/oj/components/Header.vue @@ -27,8 +27,7 @@ diff --git a/frontend/src/pages/oj/components/user/UserSetting.vue b/frontend/src/pages/oj/components/user/UserSetting.vue index 7a4be338b..7f8ab3397 100644 --- a/frontend/src/pages/oj/components/user/UserSetting.vue +++ b/frontend/src/pages/oj/components/user/UserSetting.vue @@ -1,7 +1,7 @@ @@ -56,12 +56,12 @@ export default { 'Submission', 'History', 'Contest', - 'Badge', - 'Setting' + 'Badge' ], rightTabs: [ 'Storage', - 'Group' + 'Group', + 'Setting' ], currentTab: 'Summary' } From e13fb1e58eefcc349b72386fbc3e9f6f76670fb2 Mon Sep 17 00:00:00 2001 From: jimin9038 Date: Tue, 12 Apr 2022 06:26:42 +0000 Subject: [PATCH 38/39] fix: add assertSuccess to testcase --- backend/contest/tests.py | 6 +++++- frontend/src/pages/oj/views/problem/Problem.vue | 1 - 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/backend/contest/tests.py b/backend/contest/tests.py index 20b8eabb4..ebbf082eb 100644 --- a/backend/contest/tests.py +++ b/backend/contest/tests.py @@ -267,13 +267,17 @@ def test_create_problem_bank(self): self.create_user("2018123123", "123123") url = self.reverse("contest_bank_api") response = self.client.post(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) + data = copy.deepcopy(DEFAULT_CONTEST_DATA) + data.pop("allowed_groups") + data.pop("prizes") + self.contest = Contest.objects.create(created_by=admin, **data) # create problem in contest data = copy.deepcopy(DEFAULT_PROBLEM_DATA) diff --git a/frontend/src/pages/oj/views/problem/Problem.vue b/frontend/src/pages/oj/views/problem/Problem.vue index c706ded3e..ecdd6ceff 100644 --- a/frontend/src/pages/oj/views/problem/Problem.vue +++ b/frontend/src/pages/oj/views/problem/Problem.vue @@ -348,7 +348,6 @@ export default { ProblemSidebar, login, register, - profileSetting, Table, VueResizable, UserSetting From bd82fcd76335d064ca0fa41b72e1e824f8395d88 Mon Sep 17 00:00:00 2001 From: goo314 Date: Tue, 19 Apr 2022 12:12:06 +0900 Subject: [PATCH 39/39] fix: Delete unnecessary file and edit gitignore --- ...787bcb (feat: Add test for user-contest-api) | Bin 8196 -> 0 bytes .gitignore | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 .DS_Store~1787bcb (feat: Add test for user-contest-api) diff --git a/.DS_Store~1787bcb (feat: Add test for user-contest-api) b/.DS_Store~1787bcb (feat: Add test for user-contest-api) deleted file mode 100644 index 7c927092288a2f87bb116e6afc60ad78d46701f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8196 zcmeHLziSjh6ntGlC#>NjuZ?%rl^PfZ~K-@wdw%zSU=tdqYHKwj0e5O%YKQ z)#Co$Xhsw}Zfl|J@I3+0AfBj6DYa;suF$mtTRYSNbwC|Z2h;&|U^_T~Z?-MflJCA7 z)m9x)2mVV3?D-I&S`0mA4*BRn!zlo865U+z8+ic71Rg_=nL`f6oGyDXKGgUW!?mG)w^qNpL|_^b>?>w(O*U^ElNc%Fg;F?8ZF?)VOgp*z2_I@ znAEoV^Te0apIF-dSLZGSUXaAM#5+L2KlspDh3a{kR{)=`*C>KVtAx(?UwSY3Ot zD=EMEmnh+1U5c?+9qA7pxOZ+GSL!_Wx_9dK2J0-HTk8E2zSK>JZt