├── .devcontainer
├── .gitignore
├── Dockerfile
├── devcontainer.json
├── docker-compose.yml
└── postCreateCommand.sh
├── .dockerignore
├── .editorconfig
├── .github
└── workflows
│ ├── build.yml
│ ├── test-backend.yml
│ └── test-frontend.yml
├── .gitignore
├── .gitpod.docker-compose.yml
├── .gitpod.yml
├── .vscode
├── extensions.json
├── launch.json
└── settings.json
├── Dockerfile
├── LICENSE
├── README.md
├── backend
├── .coveragerc
├── .flake8
├── account
│ ├── __init__.py
│ ├── fixtures
│ │ └── data.yaml
│ ├── middleware.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ ├── 0002_auto_20170209_1028.py
│ │ ├── 0003_userprofile_total_score.py
│ │ ├── 0005_auto_20170830_1154.py
│ │ ├── 0006_user_session_keys.py
│ │ ├── 0008_auto_20171011_1214.py
│ │ ├── 0009_auto_20171125_1514.py
│ │ ├── 0010_auto_20180501_0436.py
│ │ ├── 0011_auto_20180501_0456.py
│ │ ├── 0012_userprofile_language.py
│ │ ├── 0013_auto_20210117_1246.py
│ │ ├── 0014_auto_20210126_1738.py
│ │ ├── 0015_auto_20210703_1545.py
│ │ ├── 0016_auto_20210703_1559.py
│ │ ├── 0017_auto_20210703_1612.py
│ │ ├── 0018_auto_20211030_1535.py
│ │ └── __init__.py
│ ├── models.py
│ ├── serializers.py
│ ├── tasks.py
│ ├── templates
│ │ ├── email_auth.html
│ │ └── reset_password_email.html
│ ├── tests.py
│ ├── urls
│ │ ├── __init__.py
│ │ ├── admin.py
│ │ └── oj.py
│ └── views
│ │ ├── __init__.py
│ │ ├── admin.py
│ │ └── oj.py
├── announcement
│ ├── __init__.py
│ ├── fixtures
│ │ └── data.yaml
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ ├── 0002_auto_20171011_1214.py
│ │ ├── 0003_auto_20180501_0436.py
│ │ ├── 0004_alter_announcement_id.py
│ │ ├── 0005_announcement_top_fixed.py
│ │ └── __init__.py
│ ├── models.py
│ ├── serializers.py
│ ├── tests.py
│ ├── urls
│ │ ├── __init__.py
│ │ ├── admin.py
│ │ └── oj.py
│ └── views
│ │ ├── __init__.py
│ │ ├── admin.py
│ │ └── oj.py
├── assignment
│ ├── __init__.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ ├── 0002_alter_assignment_id.py
│ │ └── __init__.py
│ ├── models.py
│ ├── serializers.py
│ ├── tests.py
│ ├── urls
│ │ ├── professor.py
│ │ └── student.py
│ └── views
│ │ ├── professor.py
│ │ └── student.py
├── banner
│ ├── __init__.py
│ ├── fixtures
│ │ └── data.yaml
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ └── __init__.py
│ ├── models.py
│ ├── serializers.py
│ ├── tests.py
│ ├── urls
│ │ ├── __init__.py
│ │ ├── admin.py
│ │ └── oj.py
│ └── views
│ │ ├── __init__.py
│ │ ├── admin.py
│ │ └── oj.py
├── conf
│ ├── __init__.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ ├── 0002_auto_20171011_1214.py
│ │ ├── 0003_judgeserver_is_disabled.py
│ │ ├── 0004_auto_20180501_0436.py
│ │ ├── 0005_alter_judgeserver_id.py
│ │ └── __init__.py
│ ├── models.py
│ ├── serializers.py
│ ├── tests.py
│ ├── urls
│ │ ├── __init__.py
│ │ ├── admin.py
│ │ ├── oj.py
│ │ └── professor.py
│ └── views.py
├── contest
│ ├── __init__.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ ├── 0002_auto_20170209_0845.py
│ │ ├── 0003_auto_20170217_0820.py
│ │ ├── 0004_auto_20170717_1324.py
│ │ ├── 0005_auto_20170823_0918.py
│ │ ├── 0006_auto_20171011_1214.py
│ │ ├── 0007_contestannouncement_visible.py
│ │ ├── 0008_contest_allowed_ip_ranges.py
│ │ ├── 0009_auto_20180501_0436.py
│ │ ├── 0010_auto_20190326_0201.py
│ │ ├── 0011_auto_20211030_1535.py
│ │ ├── 0012_rename_total_score_acmcontestrank_total_penalty.py
│ │ ├── 0013_contestannouncement_problem.py
│ │ ├── 0014_auto_20220211_1432.py
│ │ ├── 0015_auto_20220211_1433.py
│ │ ├── 0016_rename_prize_contest_prizes.py
│ │ ├── 0017_alter_contest_allowed_groups.py
│ │ ├── 0018_auto_20220225_1422.py
│ │ ├── 0019_alter_contestprize_contest.py
│ │ ├── 0020_auto_20220315_1654.py
│ │ ├── 0021_contest_rank_penalty_visible.py
│ │ └── __init__.py
│ ├── models.py
│ ├── serializers.py
│ ├── tests.py
│ ├── urls
│ │ ├── __init__.py
│ │ ├── admin.py
│ │ └── oj.py
│ └── views
│ │ ├── __init__.py
│ │ ├── admin.py
│ │ └── oj.py
├── course
│ ├── __init__.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ ├── 0002_auto_20210808_1709.py
│ │ ├── 0003_auto_20211226_1458.py
│ │ ├── 0004_registration_bookmark.py
│ │ └── __init__.py
│ ├── models.py
│ ├── serializers.py
│ ├── tests.py
│ ├── urls
│ │ ├── __init__.py
│ │ ├── professor.py
│ │ └── student.py
│ └── views
│ │ ├── __init__.py
│ │ ├── professor.py
│ │ └── student.py
├── data
│ ├── .gitignore
│ ├── config
│ │ └── .gitkeep
│ ├── log
│ │ └── .gitkeep
│ ├── public
│ │ ├── avatar
│ │ │ └── default.png
│ │ ├── upload
│ │ │ ├── banner1.png
│ │ │ ├── banner2.png
│ │ │ ├── banner3.png
│ │ │ └── banner4.png
│ │ └── website
│ │ │ └── favicon.ico
│ ├── ssl
│ │ └── .gitkeep
│ └── test_case
│ │ └── .gitkeep
├── deploy
│ ├── entrypoint.sh
│ ├── health_check.py
│ ├── nginx
│ │ ├── api_proxy.conf
│ │ ├── locations.conf
│ │ └── nginx.conf
│ ├── requirements.txt
│ ├── supervisord.conf
│ └── test_case_rsync
│ │ ├── Dockerfile
│ │ ├── docker-compose.yml
│ │ ├── rsyncd.conf
│ │ └── run.sh
├── docs
│ └── data.json
├── group
│ ├── __init__.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ ├── 0002_alter_groupmember_is_admin.py
│ │ ├── 0003_auto_20220203_2234.py
│ │ ├── 0004_rename_is_admin_groupmember_is_group_admin.py
│ │ └── __init__.py
│ ├── models.py
│ ├── serializers.py
│ ├── tests.py
│ ├── urls
│ │ ├── __init__.py
│ │ ├── admin.py
│ │ └── oj.py
│ └── views
│ │ ├── __init__.py
│ │ ├── admin.py
│ │ └── oj.py
├── judge
│ ├── __init__.py
│ ├── dispatcher.py
│ ├── languages.py
│ └── tasks.py
├── manage.py
├── oj
│ ├── __init__.py
│ ├── asgi.py
│ ├── dev_settings.py
│ ├── production_settings.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
├── options
│ ├── __init__.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ ├── 0002_auto_20180501_0436.py
│ │ ├── 0003_migrate_languages_options.py
│ │ ├── 0004_auto_20211030_1535.py
│ │ └── __init__.py
│ ├── models.py
│ ├── options.py
│ ├── tests.py
│ └── views.py
├── problem
│ ├── __init__.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ ├── 0002_problem__id.py
│ │ ├── 0003_auto_20170217_0820.py
│ │ ├── 0004_auto_20170501_0637.py
│ │ ├── 0005_auto_20170815_1258.py
│ │ ├── 0006_auto_20170823_0918.py
│ │ ├── 0008_auto_20170923_1318.py
│ │ ├── 0009_auto_20171011_1214.py
│ │ ├── 0010_problem_spj_compile_ok.py
│ │ ├── 0011_fix_problem_ac_count.py
│ │ ├── 0012_auto_20180501_0436.py
│ │ ├── 0013_problem_io_mode.py
│ │ ├── 0014_problem_share_submission.py
│ │ ├── 0015_auto_20210730_2244.py
│ │ ├── 0016_auto_20210801_2045.py
│ │ ├── 0017_auto_20210814_1415.py
│ │ ├── 0018_auto_20211226_1458.py
│ │ ├── 0019_problem_bank.py
│ │ ├── 0020_auto_20220327_2013.py
│ │ ├── 0021_auto_20220329_1451.py
│ │ ├── 0022_alter_problem_problem_set.py
│ │ ├── 0023_auto_20220329_1517.py
│ │ ├── 0024_alter_problemset_problems.py
│ │ ├── 0025_alter_problemset_problem_set_group.py
│ │ └── __init__.py
│ ├── models.py
│ ├── serializers.py
│ ├── tests.py
│ ├── urls
│ │ ├── __init__.py
│ │ ├── admin.py
│ │ ├── oj.py
│ │ ├── professor.py
│ │ └── student.py
│ ├── utils.py
│ └── views
│ │ ├── __init__.py
│ │ ├── admin.py
│ │ ├── oj.py
│ │ ├── professor.py
│ │ └── student.py
├── qna
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ ├── 0002_question_content_question_course_id_and_more.py
│ │ ├── 0003_alter_question_content_alter_question_course_id_and_more.py
│ │ ├── 0004_rename_course_id_question_course_and_more.py
│ │ ├── 0005_rename_status_question_is_open.py
│ │ └── __init__.py
│ ├── models.py
│ ├── serializers.py
│ ├── tests.py
│ ├── urls
│ │ ├── professor.py
│ │ └── student.py
│ └── views
│ │ ├── professor.py
│ │ └── student.py
├── run_test.py
├── submission
│ ├── __init__.py
│ ├── migrations
│ │ ├── 0001_initial.py
│ │ ├── 0002_auto_20170509_1203.py
│ │ ├── 0005_submission_username.py
│ │ ├── 0006_auto_20170830_1154.py
│ │ ├── 0007_auto_20170923_1318.py
│ │ ├── 0008_submission_ip.py
│ │ ├── 0009_delete_user_output.py
│ │ ├── 0011_fix_submission_number.py
│ │ ├── 0012_auto_20180501_0436.py
│ │ ├── 0013_auto_20210730_2244.py
│ │ ├── 0014_auto_20210802_2115.py
│ │ ├── 0015_remove_submission_score.py
│ │ ├── 0016_auto_20211226_1458.py
│ │ └── __init__.py
│ ├── models.py
│ ├── serializers.py
│ ├── tests.py
│ ├── urls.py
│ └── views.py
└── utils
│ ├── __init__.py
│ ├── api
│ ├── __init__.py
│ ├── _serializers.py
│ ├── api.py
│ └── tests.py
│ ├── cache.py
│ ├── captcha
│ ├── Menlo.ttc
│ ├── __init__.py
│ ├── timesbi.ttf
│ └── views.py
│ ├── constants.py
│ ├── decorators.py
│ ├── management
│ ├── __init__.py
│ └── commands
│ │ ├── __init__.py
│ │ └── inituser.py
│ ├── migrate_data.py
│ ├── models.py
│ ├── serializers.py
│ ├── shortcuts.py
│ ├── tasks.py
│ ├── throttling.py
│ ├── urls.py
│ ├── views.py
│ └── xss_filter.py
├── db_backup.sh
├── docker-compose.yml
└── frontend
├── .browserslistrc
├── .editorconfig
├── .eslintrc.js
├── babel.config.js
├── cypress.json
├── cypress
├── fixtures
│ ├── example.json
│ └── problem-example.json
├── integration
│ ├── admin
│ │ └── problem
│ │ │ └── admin_problem.spec.js
│ └── oj
│ │ └── problem
│ │ └── problem.spec.js
├── plugins
│ └── index.js
└── support
│ ├── commands.js
│ └── index.js
├── package.json
├── postcss.config.js
├── public
├── cache.dll.json
├── dll
│ ├── dll.c9dc8146.dll.js
│ └── dll.manifest.json
└── loader.css
├── src
├── assets
│ ├── Cup.png
│ ├── icons
│ │ ├── github.svg
│ │ ├── kakao.svg
│ │ ├── link.svg
│ │ └── mail.svg
│ ├── logos
│ │ ├── codingPlatformLogo.png
│ │ ├── logo.png
│ │ ├── logo.svg
│ │ ├── signature.png
│ │ └── signature.svg
│ └── standing.png
├── fonts
│ ├── Manrope-Bold.ttf
│ ├── Manrope-ExtraBold.ttf
│ ├── Manrope-ExtraLight.ttf
│ ├── Manrope-Light.ttf
│ ├── Manrope-Medium.ttf
│ ├── Manrope-Regular.ttf
│ └── Manrope-SemiBold.ttf
├── pages
│ ├── admin
│ │ ├── App.vue
│ │ ├── api.js
│ │ ├── components
│ │ │ ├── Accordion.vue
│ │ │ ├── CodeMirror.vue
│ │ │ ├── KatexEditor.vue
│ │ │ ├── ManageProblemModal.vue
│ │ │ ├── Panel.vue
│ │ │ ├── ProblemSetGroupModal.vue
│ │ │ ├── ProblemSetModal.vue
│ │ │ ├── SideMenu.vue
│ │ │ ├── Tiptap.vue
│ │ │ ├── btn
│ │ │ │ ├── Cancel.vue
│ │ │ │ ├── IconBtn.vue
│ │ │ │ └── Save.vue
│ │ │ └── infoCard.vue
│ │ ├── index.html
│ │ ├── index.js
│ │ ├── router.js
│ │ └── views
│ │ │ ├── Home.vue
│ │ │ ├── contest
│ │ │ ├── Contest.vue
│ │ │ └── ContestList.vue
│ │ │ ├── general
│ │ │ ├── Announcement.vue
│ │ │ ├── Banner.vue
│ │ │ ├── Conf.vue
│ │ │ ├── Dashboard.vue
│ │ │ ├── JudgeServer.vue
│ │ │ ├── Login.vue
│ │ │ ├── PruneTestCase.vue
│ │ │ └── User.vue
│ │ │ ├── index.js
│ │ │ └── problem
│ │ │ ├── AddPublicProblem.vue
│ │ │ ├── Problem.vue
│ │ │ ├── ProblemList.vue
│ │ │ └── ProblemSet.vue
│ ├── oj
│ │ ├── App.vue
│ │ ├── api.js
│ │ ├── components
│ │ │ ├── Banner.vue
│ │ │ ├── CodeMirror.vue
│ │ │ ├── ColorRoundButton.vue
│ │ │ ├── ContestInformation.vue
│ │ │ ├── Dropdown.vue
│ │ │ ├── Footer.vue
│ │ │ ├── Header.vue
│ │ │ ├── Highlight.vue
│ │ │ ├── Modal.vue
│ │ │ ├── NeonBox.vue
│ │ │ ├── PageTitle.vue
│ │ │ ├── PageTop.vue
│ │ │ ├── ProblemSetGroup.vue
│ │ │ ├── ShadowRoundButton.vue
│ │ │ ├── Sidemenu.vue
│ │ │ ├── StatusBadge.vue
│ │ │ ├── Table.vue
│ │ │ └── mixins
│ │ │ │ ├── emitter.js
│ │ │ │ ├── form.js
│ │ │ │ ├── index.js
│ │ │ │ └── problem.js
│ │ ├── index.html
│ │ ├── index.js
│ │ ├── router
│ │ │ ├── index.js
│ │ │ └── routes.js
│ │ └── views
│ │ │ ├── announcement
│ │ │ ├── Announcement.vue
│ │ │ └── AnnouncementList.vue
│ │ │ ├── contest
│ │ │ ├── ContestClarification.vue
│ │ │ ├── ContestDetail.vue
│ │ │ ├── ContestList.vue
│ │ │ ├── ContestProblemList.vue
│ │ │ └── ContestRanking.vue
│ │ │ ├── general
│ │ │ ├── 404.vue
│ │ │ └── Home.vue
│ │ │ ├── index.js
│ │ │ ├── lecture
│ │ │ ├── LectureAssignmentDetail.vue
│ │ │ ├── LectureAssignmentList.vue
│ │ │ ├── LectureDashboard.vue
│ │ │ ├── LectureList.vue
│ │ │ ├── LectureQnA.vue
│ │ │ └── LectureQnADetail.vue
│ │ │ ├── problem
│ │ │ ├── Problem.vue
│ │ │ ├── ProblemList.vue
│ │ │ ├── ProblemSet.vue
│ │ │ └── ProblemSidebar.vue
│ │ │ └── user
│ │ │ ├── ApplyResetPassword.vue
│ │ │ ├── DeleteAccount.vue
│ │ │ ├── EmailAuth.vue
│ │ │ ├── Login.vue
│ │ │ ├── Logout.vue
│ │ │ ├── ProfileSetting.vue
│ │ │ ├── Register.vue
│ │ │ ├── ResetPassword.vue
│ │ │ └── SendEmailAuth.vue
│ └── professor
│ │ ├── App.vue
│ │ ├── api.js
│ │ ├── components
│ │ ├── Accordion.vue
│ │ ├── CodeMirror.vue
│ │ ├── KatexEditor.vue
│ │ ├── Panel.vue
│ │ ├── SideMenu.vue
│ │ ├── Tiptap.vue
│ │ ├── btn
│ │ │ ├── Cancel.vue
│ │ │ ├── IconBtn.vue
│ │ │ └── Save.vue
│ │ └── infoCard.vue
│ │ ├── index.html
│ │ ├── index.js
│ │ ├── router.js
│ │ └── views
│ │ ├── Home.vue
│ │ ├── assignment
│ │ ├── AssignmentList.vue
│ │ ├── CreateAssignment.vue
│ │ └── ImportPublicProblem.vue
│ │ ├── general
│ │ ├── CourseBookmark.vue
│ │ ├── CourseDashboard.vue
│ │ ├── CourseModal.vue
│ │ ├── Dashboard.vue
│ │ └── Login.vue
│ │ ├── index.js
│ │ ├── problem
│ │ ├── Problem.vue
│ │ ├── ProblemGrade.vue
│ │ └── SubmissionDetail.vue
│ │ ├── qna
│ │ └── QnA.vue
│ │ └── users
│ │ ├── RegisterNewUser.vue
│ │ └── UserList.vue
├── plugins
│ └── highlight.js
├── store
│ ├── index.js
│ ├── modules
│ │ ├── contest.js
│ │ ├── course.js
│ │ ├── group.js
│ │ └── user.js
│ └── types.js
├── styles
│ ├── bootstrap.scss
│ ├── common.scss
│ ├── markdown.scss
│ ├── tailwind.css
│ └── tiptapview.scss
└── utils
│ ├── constants.js
│ ├── filters.js
│ ├── storage.js
│ ├── time.js
│ └── utils.js
├── tailwind.config.js
├── vue.config.js
└── yarn.lock
/.devcontainer/.gitignore:
--------------------------------------------------------------------------------
1 | judge-server
2 |
--------------------------------------------------------------------------------
/.devcontainer/Dockerfile:
--------------------------------------------------------------------------------
1 | # [Choice] Python version (https://github.com/microsoft/vscode-dev-containers/tree/main/containers/python-3)
2 | ARG VARIANT=3.8-bullseye
3 | FROM mcr.microsoft.com/vscode/devcontainers/python:${VARIANT}
4 |
5 | # [Choice] Node.js version: none, lts/*, 16, 14, 12, 10
6 | ARG NODE_VERSION="16"
7 | RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi
8 |
9 | RUN apt-get update && \
10 | export DEBIAN_FRONTEND=noninteractive && \
11 | apt-get -y install --no-install-recommends \
12 | libgtk2.0-0 \
13 | libgtk-3-0 \
14 | libgbm-dev \
15 | libnotify-dev \
16 | libgconf-2-4 \
17 | libnss3 \
18 | libxss1 \
19 | libasound2 \
20 | libxtst6 xauth xvfb
21 |
--------------------------------------------------------------------------------
/.devcontainer/devcontainer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Coding Platform",
3 | "dockerComposeFile": "docker-compose.yml",
4 | "service": "app",
5 | "workspaceFolder": "/workspace",
6 | "forwardPorts": [10000],
7 |
8 | "postCreateCommand": "./.devcontainer/postCreateCommand.sh",
9 |
10 | "extensions": [
11 | "bradlc.vscode-tailwindcss",
12 | "dbaeumer.vscode-eslint",
13 | "donjayamanne.githistory",
14 | "eamodio.gitlens",
15 | "editorconfig.editorconfig",
16 | "gruntfuggly.todo-tree",
17 | "johnsoncodehk.volar",
18 | "mhutchie.git-graph",
19 | "ms-python.python",
20 | "naumovs.color-highlight",
21 | "oderwat.indent-rainbow",
22 | "pkief.material-icon-theme",
23 | "rangav.vscode-thunder-client"
24 | ],
25 |
26 | // Connect as non-root user (https://code.visualstudio.com/remote/advancedcontainers/add-nonroot-user)
27 | "remoteUser": "vscode"
28 | }
29 |
--------------------------------------------------------------------------------
/.devcontainer/postCreateCommand.sh:
--------------------------------------------------------------------------------
1 | #! /bin/bash
2 | set -x
3 |
4 | # Install Python packages
5 | python3 -m pip install wheel
6 | python3 -m pip install -r /workspace/backend/deploy/requirements.txt
7 |
8 | # Django Setup
9 | echo `cat /dev/urandom | head -1 | md5sum | head -c 32` > /workspace/backend/data/config/secret.key
10 | python3 /workspace/backend/manage.py migrate
11 | python3 /workspace/backend/manage.py inituser --username root --password rootroot --action create_super_admin
12 |
13 | # Add `judge-server-dev` container to database
14 | echo "
15 | from conf.models import JudgeServer
16 | from django.utils import timezone
17 |
18 | JudgeServer.objects.create(
19 | hostname='hostname',
20 | judger_version='version',
21 | cpu_core=1,
22 | memory_usage=0,
23 | cpu_usage=0,
24 | ip='127.0.0.1',
25 | service_url='$JUDGE_SERVER_URL',
26 | last_heartbeat=timezone.now()
27 | )
28 | " | python3 /workspace/backend/manage.py shell
29 |
30 | # Register judge server token
31 | echo "
32 | from options.options import SysOptions
33 | SysOptions.judge_server_token='$JUDGE_SERVER_TOKEN'
34 | " | python3 /workspace/backend/manage.py shell
35 |
36 | # Install Node packages
37 | yarn --cwd /workspace/frontend install
38 | yarn --cwd /workspace/frontend cypress install
39 |
--------------------------------------------------------------------------------
/.dockerignore:
--------------------------------------------------------------------------------
1 | # Container Volume Mount
2 | /data
3 |
4 | # Python Virtual Environment
5 | **/.env
6 | **/.venv
7 | **/env/
8 | **/venv/
9 | **/ENV/
10 | **/env.bak/
11 | **/venv.bak/
12 | **/pythonenv*
13 |
14 | # IDE Setting
15 | **/.idea
16 | **/.vscode
17 |
18 | # Git
19 | .git
20 |
21 | # Mac OS
22 | **/.DS_Store
23 |
24 | # Node Packages
25 | **/node_modules
26 |
27 | # Vue CLI DLL Plugin
28 | frontend/public
29 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*]
2 | indent_style = space
3 | end_of_line = lf
4 | insert_final_newline = true
5 | trim_trailing_whitespace = true
6 |
7 | [*.{html,css,js,json,vue,yml}]
8 | indent_size = 2
9 |
10 | [*.{py,sh,conf}]
11 | indent_size = 4
12 |
13 | [*.md]
14 | trim_trailing_whitespace = false
15 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build Container Image
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 |
8 | jobs:
9 | build:
10 | runs-on: ubuntu-latest
11 |
12 | steps:
13 | - name: Checkout to repository
14 | uses: actions/checkout@v2
15 |
16 | - name: Set up Docker Buildx
17 | uses: docker/setup-buildx-action@v1
18 |
19 | - name: Login to GitHub Container Registry
20 | uses: docker/login-action@v1
21 | with:
22 | registry: ghcr.io
23 | username: ${{ github.actor }}
24 | password: ${{ secrets.GITHUB_TOKEN }}
25 |
26 | - name: Build and push container image
27 | uses: docker/build-push-action@v2
28 | with:
29 | push: true
30 | tags: ghcr.io/skkuding/coding-platform:latest
31 |
--------------------------------------------------------------------------------
/.github/workflows/test-frontend.yml:
--------------------------------------------------------------------------------
1 | name: Frontend Tests
2 |
3 | # Controls when the action will run.
4 | on:
5 | # Triggers the workflow on push or pull request events but only for the master branch
6 | pull_request:
7 | paths:
8 | - "frontend/**"
9 | - ".github/workflows/test-frontend.yml"
10 |
11 | # Allows you to run this workflow manually from the Actions tab
12 | workflow_dispatch:
13 |
14 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel
15 | jobs:
16 | test:
17 | runs-on: ubuntu-18.04
18 | env:
19 | working-directory: ./frontend
20 |
21 | steps:
22 | - uses: actions/checkout@v2
23 |
24 | - uses: actions/setup-node@v2
25 | with:
26 | node-version: "14"
27 |
28 | - name: Cache Node modules
29 | id: cache-yarn
30 | uses: actions/cache@v2
31 | with:
32 | path: |
33 | ./frontend/node_modules
34 | ./frontend/public
35 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
36 | restore-keys: |
37 | ${{ runner.os }}-yarn-
38 |
39 | - name: Install Node packages
40 | if: steps.cache-yarn.outputs.cache-hit != 'true'
41 | working-directory: ${{ env.working-directory }}
42 | run: yarn install
43 |
44 | - name: Check styles
45 | working-directory: ${{ env.working-directory }}
46 | run: yarn lint --no-fix
47 |
48 | - name: Build production files
49 | working-directory: ${{ env.working-directory }}
50 | run: yarn build
51 |
--------------------------------------------------------------------------------
/.gitpod.docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3'
2 |
3 | services:
4 | db:
5 | image: postgres:12-alpine
6 | restart: unless-stopped
7 | volumes:
8 | - postgres-data:/var/lib/postgresql/data
9 | environment:
10 | - POSTGRES_PASSWORD=codingplatform
11 | - POSTGRES_USER=codingplatform
12 | - POSTGRES_DB=codingplatform
13 | ports:
14 | - "0.0.0.0:5432:5432"
15 |
16 | redis:
17 | image: redis:4.0-alpine
18 | restart: unless-stopped
19 | volumes:
20 | - redis-data:/data
21 | ports:
22 | - "0.0.0.0:6379:6379"
23 |
24 | volumes:
25 | postgres-data: null
26 | redis-data: null
27 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "ms-vscode-remote.remote-containers"
4 | ],
5 | "unwantedRecommendations": [
6 | "esbenp.prettier-vscode"
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | "name": "Vue.js: Chrome",
6 | "type": "chrome",
7 | "request": "launch",
8 | "url": "http://localhost:8080",
9 | "webRoot": "${workspaceFolder}/frontend/src",
10 | "breakOnLoad": true,
11 | "sourceMapPathOverrides": {
12 | "webpack:///src/*": "${webRoot}/*"
13 | }
14 | },
15 | {
16 | "name": "Vue.js: Edge",
17 | "type": "msedge",
18 | "request": "launch",
19 | "url": "http://localhost:8080",
20 | "webRoot": "${workspaceFolder}/frontend/src",
21 | "breakOnLoad": true,
22 | "sourceMapPathOverrides": {
23 | "webpack:///src/*": "${webRoot}/*"
24 | }
25 | },
26 | {
27 | "name": "Python: Django",
28 | "type": "python",
29 | "request": "launch",
30 | "program": "${workspaceFolder}/backend/manage.py",
31 | "args": [
32 | "runserver",
33 | "--noreload"
34 | ],
35 | "django": true
36 | }
37 | ]
38 | }
39 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.bracketPairColorization.enabled": true,
3 | "editor.codeActionsOnSave": {
4 | "source.fixAll.eslint": true
5 | },
6 | "editor.formatOnSave": true,
7 | "eslint.workingDirectories": [
8 | "frontend/"
9 | ],
10 | "files.autoSave": "off",
11 | "material-icon-theme.activeIconPack": "vue_vuex",
12 | "prettier.enable": false,
13 | "python.formatting.provider": "none",
14 | "python.linting.flake8Args": [
15 | "--config",
16 | "backend/.flake8"
17 | ],
18 | "python.linting.flake8Enabled": true,
19 | "[javascript][typescript][vue]": {
20 | "editor.formatOnSave": false
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # Build Stage
2 | FROM node:14-alpine AS builder
3 |
4 | ADD ./frontend /build
5 | WORKDIR /build
6 |
7 | RUN yarn install && \
8 | yarn build
9 |
10 | # Deploy Stage
11 | FROM python:3.8.12-alpine3.15
12 |
13 | ENV OJ_ENV production
14 | ENV NODE_ENV production
15 |
16 | ADD ./backend /app
17 | WORKDIR /app
18 |
19 | HEALTHCHECK --interval=5s --retries=3 CMD python /app/deploy/health_check.py
20 |
21 | RUN apk add --update --no-cache build-base nginx openssl curl unzip supervisor jpeg-dev zlib-dev postgresql-dev freetype-dev && \
22 | pip install --no-cache-dir -r /app/deploy/requirements.txt && \
23 | apk del build-base --purge
24 |
25 | COPY --from=builder /build/dist /app/dist
26 |
27 | ENTRYPOINT /app/deploy/entrypoint.sh
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020-present skkuding
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/backend/.coveragerc:
--------------------------------------------------------------------------------
1 | [run]
2 | omit = */urls/*
3 | */__init__.py
4 | */tests.py
5 | */migrations/*
6 | *urls.py
7 | utils/xss_filter.py
8 |
--------------------------------------------------------------------------------
/backend/.flake8:
--------------------------------------------------------------------------------
1 | [flake8]
2 | exclude =
3 | xss_filter.py,
4 | */migrations/,
5 | *settings.py,
6 | */apps.py,
7 | .venv/,
8 | venv/
9 | max-line-length = 180
10 | inline-quotes = "
11 | no-accept-encodings = True
12 |
--------------------------------------------------------------------------------
/backend/account/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/account/__init__.py
--------------------------------------------------------------------------------
/backend/account/middleware.py:
--------------------------------------------------------------------------------
1 | from django.conf import settings
2 | from django.db import connection
3 | from django.utils.timezone import now
4 | from django.utils.deprecation import MiddlewareMixin
5 |
6 | from utils.api import JSONResponse
7 |
8 |
9 | class SessionRecordMiddleware(MiddlewareMixin):
10 | def process_request(self, request):
11 | request.ip = request.META.get(settings.IP_HEADER, request.META.get("REMOTE_ADDR"))
12 | if request.user.is_authenticated:
13 | session = request.session
14 | session["user_agent"] = request.META.get("HTTP_USER_AGENT", "")
15 | session["ip"] = request.ip
16 | session["last_activity"] = now()
17 | user_sessions = request.user.session_keys
18 | if session.session_key not in user_sessions:
19 | user_sessions.append(session.session_key)
20 | request.user.save()
21 |
22 |
23 | class AdminRoleRequiredMiddleware(MiddlewareMixin):
24 | def process_request(self, request):
25 | path = request.path_info
26 | if path.startswith("/admin/") or path.startswith("/api/admin/"):
27 | if not (request.user.is_authenticated and request.user.is_admin_role()):
28 | return JSONResponse.response({"error": "login-required", "data": "Please login in first"})
29 |
30 |
31 | class LogSqlMiddleware(MiddlewareMixin):
32 | def process_response(self, request, response):
33 | print("\033[94m", "#" * 30, "\033[0m")
34 | time_threshold = 0.03
35 | for query in connection.queries:
36 | if float(query["time"]) > time_threshold:
37 | print("\033[93m", query, "\n", "-" * 30, "\033[0m")
38 | else:
39 | print(query, "\n", "-" * 30)
40 | return response
41 |
--------------------------------------------------------------------------------
/backend/account/migrations/0002_auto_20170209_1028.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.12 on 2017-02-09 10:28
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('account', '0001_initial'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='user',
17 | name='problem_permission',
18 | field=models.CharField(default='None', max_length=24),
19 | ),
20 | migrations.AlterField(
21 | model_name='user',
22 | name='admin_type',
23 | field=models.CharField(default='Regular User', max_length=24),
24 | ),
25 | ]
26 |
--------------------------------------------------------------------------------
/backend/account/migrations/0003_userprofile_total_score.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2017-08-20 02:03
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('account', '0002_auto_20170209_1028'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='userprofile',
17 | name='total_score',
18 | field=models.BigIntegerField(default=0),
19 | ),
20 | migrations.RenameField(
21 | model_name='userprofile',
22 | old_name='accepted_problem_number',
23 | new_name='accepted_number',
24 | ),
25 | migrations.RemoveField(
26 | model_name='userprofile',
27 | name='time_zone',
28 | )
29 | ]
30 |
--------------------------------------------------------------------------------
/backend/account/migrations/0005_auto_20170830_1154.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.4 on 2017-08-30 11:54
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 | import jsonfield.fields
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | dependencies = [
12 | ('account', '0003_userprofile_total_score'),
13 | ]
14 |
15 | operations = [
16 | migrations.RenameField(
17 | model_name='userprofile',
18 | old_name='problems_status',
19 | new_name='acm_problems_status',
20 | ),
21 | migrations.AddField(
22 | model_name='userprofile',
23 | name='oi_problems_status',
24 | field=jsonfield.fields.JSONField(default={}),
25 | ),
26 | migrations.RemoveField(
27 | model_name='user',
28 | name='real_name',
29 | ),
30 | migrations.RemoveField(
31 | model_name='userprofile',
32 | name='student_id',
33 | ),
34 | migrations.AddField(
35 | model_name='userprofile',
36 | name='real_name',
37 | field=models.CharField(max_length=30, blank=True, null=True),
38 | ),
39 | ]
40 |
--------------------------------------------------------------------------------
/backend/account/migrations/0006_user_session_keys.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.4 on 2017-09-16 06:22
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 | import jsonfield.fields
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | dependencies = [
12 | ('account', '0005_auto_20170830_1154'),
13 | ]
14 |
15 | operations = [
16 | migrations.AddField(
17 | model_name='user',
18 | name='session_keys',
19 | field=jsonfield.fields.JSONField(default=[]),
20 | ),
21 | migrations.RenameField(
22 | model_name='userprofile',
23 | old_name='phone_number',
24 | new_name='github',
25 | ),
26 | migrations.AlterField(
27 | model_name='userprofile',
28 | name='avatar',
29 | field=models.CharField(default='/static/avatar/default.png', max_length=50),
30 | ),
31 | migrations.AlterField(
32 | model_name='userprofile',
33 | name='github',
34 | field=models.CharField(blank=True, max_length=50, null=True),
35 | ),
36 | ]
37 |
--------------------------------------------------------------------------------
/backend/account/migrations/0009_auto_20171125_1514.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.4 on 2017-11-25 15:14
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('account', '0008_auto_20171011_1214'),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterField(
16 | model_name='userprofile',
17 | name='avatar',
18 | field=models.CharField(default='/public/avatar/default.png', max_length=256),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/backend/account/migrations/0011_auto_20180501_0456.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.3 on 2018-05-01 04:56
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('account', '0010_auto_20180501_0436'),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterField(
16 | model_name='user',
17 | name='email',
18 | field=models.TextField(null=True),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/backend/account/migrations/0012_userprofile_language.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.4 on 2018-07-15 02:06
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('account', '0011_auto_20180501_0456'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='userprofile',
17 | name='language',
18 | field=models.TextField(null=True),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/backend/account/migrations/0013_auto_20210117_1246.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.7 on 2021-01-17 12:46
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('account', '0012_userprofile_language'),
10 | ]
11 |
12 | operations = [
13 | migrations.RemoveField(
14 | model_name='userprofile',
15 | name='major',
16 | ),
17 | migrations.RemoveField(
18 | model_name='userprofile',
19 | name='school',
20 | ),
21 | migrations.AddField(
22 | model_name='user',
23 | name='major',
24 | field=models.TextField(null=True),
25 | ),
26 | ]
27 |
--------------------------------------------------------------------------------
/backend/account/migrations/0014_auto_20210126_1738.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.7 on 2021-01-26 17:38
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('account', '0013_auto_20210117_1246'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='user',
15 | name='email_auth_token',
16 | field=models.TextField(null=True),
17 | ),
18 | migrations.AddField(
19 | model_name='user',
20 | name='has_email_auth',
21 | field=models.BooleanField(default=True),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/backend/account/migrations/0015_auto_20210703_1545.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.24 on 2021-07-03 06:45
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('account', '0014_auto_20210126_1738'),
10 | ]
11 |
12 | operations = [
13 | migrations.RemoveField(
14 | model_name='user',
15 | name='auth_token',
16 | ),
17 | migrations.RemoveField(
18 | model_name='user',
19 | name='tfa_token',
20 | ),
21 | migrations.RemoveField(
22 | model_name='user',
23 | name='two_factor_auth',
24 | ),
25 | ]
26 |
--------------------------------------------------------------------------------
/backend/account/migrations/0016_auto_20210703_1559.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.24 on 2021-07-03 06:59
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('account', '0015_auto_20210703_1545'),
10 | ]
11 |
12 | operations = [
13 | migrations.RemoveField(
14 | model_name='user',
15 | name='open_api',
16 | ),
17 | migrations.RemoveField(
18 | model_name='user',
19 | name='open_api_appkey',
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/backend/account/migrations/0017_auto_20210703_1612.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.24 on 2021-07-03 07:12
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('account', '0016_auto_20210703_1559'),
10 | ]
11 |
12 | operations = [
13 | migrations.RemoveField(
14 | model_name='userprofile',
15 | name='blog',
16 | ),
17 | migrations.RemoveField(
18 | model_name='userprofile',
19 | name='github',
20 | ),
21 | migrations.RemoveField(
22 | model_name='userprofile',
23 | name='mood',
24 | ),
25 | ]
26 |
--------------------------------------------------------------------------------
/backend/account/migrations/0018_auto_20211030_1535.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.4 on 2021-10-30 06:35
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('account', '0017_auto_20210703_1612'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='user',
15 | name='id',
16 | field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
17 | ),
18 | migrations.AlterField(
19 | model_name='user',
20 | name='session_keys',
21 | field=models.JSONField(default=list),
22 | ),
23 | migrations.AlterField(
24 | model_name='userprofile',
25 | name='acm_problems_status',
26 | field=models.JSONField(default=dict),
27 | ),
28 | migrations.AlterField(
29 | model_name='userprofile',
30 | name='id',
31 | field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
32 | ),
33 | migrations.AlterField(
34 | model_name='userprofile',
35 | name='oi_problems_status',
36 | field=models.JSONField(default=dict),
37 | ),
38 | ]
39 |
--------------------------------------------------------------------------------
/backend/account/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/account/migrations/__init__.py
--------------------------------------------------------------------------------
/backend/account/tasks.py:
--------------------------------------------------------------------------------
1 | import logging
2 | import dramatiq
3 |
4 | from options.options import SysOptions
5 | from utils.shortcuts import send_email, DRAMATIQ_WORKER_ARGS
6 |
7 | logger = logging.getLogger(__name__)
8 |
9 |
10 | @dramatiq.actor(**DRAMATIQ_WORKER_ARGS(max_retries=3))
11 | def send_email_async(from_name, to_email, to_name, subject, content):
12 | if not SysOptions.smtp_config:
13 | return
14 | try:
15 | send_email(smtp_config=SysOptions.smtp_config,
16 | from_name=from_name,
17 | to_email=to_email,
18 | to_name=to_name,
19 | subject=subject,
20 | content=content)
21 | except Exception as e:
22 | logger.exception(e)
23 |
--------------------------------------------------------------------------------
/backend/account/templates/email_auth.html:
--------------------------------------------------------------------------------
1 |
3 |
4 |
6 | {{ website_name }}
7 |
8 |
9 |
10 |
11 |
12 | Register Authentication
13 |
14 |
15 | Click below button to complete register
16 |
17 |
18 |
19 | Authenticate
20 |
21 |
22 |
23 | If you still have any questions, please contact here .
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/backend/account/templates/reset_password_email.html:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
8 | {{ website_name }}
9 |
10 |
11 |
12 |
13 |
Hello, {{ username }}:
14 |
15 | Please click {{ link }} to reset your password in 20 minutes.
16 |
17 |
18 | To protect your account, please do not use simple passwords.
19 |
20 |
21 | If you still have any questions, please contact here .
22 |
23 |
24 |
{{ website_name }}
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/backend/account/urls/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/account/urls/__init__.py
--------------------------------------------------------------------------------
/backend/account/urls/admin.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from ..views.admin import UserAdminAPI, GenerateUserAPI
4 |
5 | urlpatterns = [
6 | path("user/", UserAdminAPI.as_view(), name="user_admin_api"),
7 | path("generate_user/", GenerateUserAPI.as_view(), name="generate_user_api"),
8 | ]
9 |
--------------------------------------------------------------------------------
/backend/account/urls/oj.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from ..views.oj import (ApplyResetPasswordAPI, DeleteAccountAPI, ResetPasswordAPI,
4 | UserChangePasswordAPI, UserRegisterAPI, EmailAuthAPI, UserChangeEmailAPI,
5 | UserLoginAPI, UserLogoutAPI, UsernameOrEmailCheck,
6 | AvatarUploadAPI, UserProfileAPI, UserSettingAPI, SendEmailAuthAPI)
7 |
8 | from utils.captcha.views import CaptchaAPIView
9 |
10 | urlpatterns = [
11 | path("login/", UserLoginAPI.as_view(), name="user_login_api"),
12 | path("logout/", UserLogoutAPI.as_view(), name="user_logout_api"),
13 | path("register/", UserRegisterAPI.as_view(), name="user_register_api"),
14 | path("delete_account/", DeleteAccountAPI.as_view(), name="delete_account_api"),
15 | path("email_auth/", EmailAuthAPI.as_view(), name="email_auth_api"),
16 | path("send_email_auth/", SendEmailAuthAPI.as_view(), name="send_email_auth_api"),
17 | path("change_password/", UserChangePasswordAPI.as_view(), name="user_change_password_api"),
18 | path("change_email/", UserChangeEmailAPI.as_view(), name="user_change_email_api"),
19 | path("apply_reset_password/", ApplyResetPasswordAPI.as_view(), name="apply_reset_password_api"),
20 | path("reset_password/", ResetPasswordAPI.as_view(), name="reset_password_api"),
21 | path("captcha/", CaptchaAPIView.as_view(), name="show_captcha"),
22 | path("check_username_or_email/", UsernameOrEmailCheck.as_view(), name="check_username_or_email"),
23 | path("profile/", UserProfileAPI.as_view(), name="user_profile_api"),
24 | path("user/", UserSettingAPI.as_view(), name="user_setting_api"),
25 | path("upload_avatar/", AvatarUploadAPI.as_view(), name="avatar_upload_api"),
26 | ]
27 |
--------------------------------------------------------------------------------
/backend/account/views/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/account/views/__init__.py
--------------------------------------------------------------------------------
/backend/announcement/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/announcement/__init__.py
--------------------------------------------------------------------------------
/backend/announcement/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.12 on 2017-01-23 07:59
3 | from __future__ import unicode_literals
4 |
5 | import django.db.models.deletion
6 | from django.conf import settings
7 | from django.db import migrations, models
8 |
9 | import utils.models
10 |
11 |
12 | class Migration(migrations.Migration):
13 |
14 | initial = True
15 |
16 | dependencies = [
17 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
18 | ]
19 |
20 | operations = [
21 | migrations.CreateModel(
22 | name='Announcement',
23 | fields=[
24 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
25 | ('title', models.CharField(max_length=50)),
26 | ('content', utils.models.RichTextField()),
27 | ('create_time', models.DateTimeField(auto_now_add=True)),
28 | ('last_update_time', models.DateTimeField(auto_now=True)),
29 | ('visible', models.BooleanField(default=True)),
30 | ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
31 | ],
32 | options={
33 | 'db_table': 'announcement',
34 | },
35 | ),
36 | ]
37 |
--------------------------------------------------------------------------------
/backend/announcement/migrations/0002_auto_20171011_1214.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.4 on 2017-10-11 12:14
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('announcement', '0001_initial'),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterField(
16 | model_name='announcement',
17 | name='title',
18 | field=models.CharField(max_length=64),
19 | ),
20 | migrations.AlterModelOptions(
21 | name='announcement',
22 | options={'ordering': ('-create_time',)},
23 | ),
24 | ]
25 |
--------------------------------------------------------------------------------
/backend/announcement/migrations/0003_auto_20180501_0436.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.3 on 2018-05-01 04:36
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('announcement', '0002_auto_20171011_1214'),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterField(
16 | model_name='announcement',
17 | name='title',
18 | field=models.TextField(),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/backend/announcement/migrations/0004_alter_announcement_id.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.5 on 2021-12-26 05:58
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('announcement', '0003_auto_20180501_0436'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='announcement',
15 | name='id',
16 | field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/announcement/migrations/0005_announcement_top_fixed.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.5 on 2021-12-31 08:40
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('announcement', '0004_alter_announcement_id'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='announcement',
15 | name='top_fixed',
16 | field=models.BooleanField(default=False),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/announcement/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/announcement/migrations/__init__.py
--------------------------------------------------------------------------------
/backend/announcement/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | from account.models import User
4 | from utils.models import RichTextField
5 |
6 |
7 | class Announcement(models.Model):
8 | title = models.TextField()
9 | # HTML
10 | content = RichTextField()
11 | create_time = models.DateTimeField(auto_now_add=True)
12 | created_by = models.ForeignKey(User, on_delete=models.CASCADE)
13 | last_update_time = models.DateTimeField(auto_now=True)
14 | visible = models.BooleanField(default=True)
15 | top_fixed = models.BooleanField(default=False)
16 |
17 | class Meta:
18 | db_table = "announcement"
19 | ordering = ("-create_time",)
20 |
--------------------------------------------------------------------------------
/backend/announcement/serializers.py:
--------------------------------------------------------------------------------
1 | from utils.api import serializers
2 | from utils.api._serializers import UsernameSerializer
3 |
4 | from .models import Announcement
5 |
6 |
7 | class CreateAnnouncementSerializer(serializers.Serializer):
8 | title = serializers.CharField(max_length=64)
9 | content = serializers.CharField(max_length=1024 * 1024 * 8)
10 | visible = serializers.BooleanField()
11 | top_fixed = serializers.BooleanField()
12 |
13 |
14 | class AnnouncementSerializer(serializers.ModelSerializer):
15 | created_by = UsernameSerializer()
16 |
17 | class Meta:
18 | model = Announcement
19 | fields = "__all__"
20 |
21 |
22 | class EditAnnouncementSerializer(serializers.Serializer):
23 | id = serializers.IntegerField()
24 | title = serializers.CharField(max_length=64)
25 | content = serializers.CharField(max_length=1024 * 1024 * 8)
26 | visible = serializers.BooleanField()
27 | top_fixed = serializers.BooleanField()
28 |
--------------------------------------------------------------------------------
/backend/announcement/urls/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/announcement/urls/__init__.py
--------------------------------------------------------------------------------
/backend/announcement/urls/admin.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from ..views.admin import AnnouncementAdminAPI
4 |
5 | urlpatterns = [
6 | path("announcement/", AnnouncementAdminAPI.as_view(), name="announcement_admin_api"),
7 | ]
8 |
--------------------------------------------------------------------------------
/backend/announcement/urls/oj.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from ..views.oj import AnnouncementAPI, AnnouncementDetailAPI
4 |
5 | urlpatterns = [
6 | path("announcement/", AnnouncementAPI.as_view(), name="announcement_api"),
7 | path("announcement_detail/", AnnouncementDetailAPI.as_view(), name="announcement_detail_api"),
8 | ]
9 |
--------------------------------------------------------------------------------
/backend/announcement/views/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/announcement/views/__init__.py
--------------------------------------------------------------------------------
/backend/assignment/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/assignment/__init__.py
--------------------------------------------------------------------------------
/backend/assignment/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.24 on 2021-07-30 13:44
2 |
3 | from django.conf import settings
4 | from django.db import migrations, models
5 | import django.db.models.deletion
6 | import utils.models
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | initial = True
12 |
13 | dependencies = [
14 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
15 | ('course', '0001_initial'),
16 | ]
17 |
18 | operations = [
19 | migrations.CreateModel(
20 | name='Assignment',
21 | fields=[
22 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
23 | ('title', models.TextField()),
24 | ('content', utils.models.RichTextField()),
25 | ('start_time', models.DateTimeField()),
26 | ('end_time', models.DateTimeField()),
27 | ('create_time', models.DateTimeField(auto_now_add=True)),
28 | ('last_update_time', models.DateTimeField(auto_now=True)),
29 | ('visible', models.BooleanField(default=True)),
30 | ('course', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='course.Course')),
31 | ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
32 | ],
33 | options={
34 | 'db_table': 'assignment',
35 | 'ordering': ('-start_time',),
36 | },
37 | ),
38 | ]
39 |
--------------------------------------------------------------------------------
/backend/assignment/migrations/0002_alter_assignment_id.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.5 on 2021-12-26 05:58
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('assignment', '0001_initial'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='assignment',
15 | name='id',
16 | field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/assignment/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/assignment/migrations/__init__.py
--------------------------------------------------------------------------------
/backend/assignment/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.utils.timezone import now
3 |
4 | from course.models import Course
5 | from account.models import User
6 | from utils.models import RichTextField
7 | from utils.constants import AssignmentStatus
8 |
9 |
10 | class Assignment(models.Model):
11 | created_by = models.ForeignKey(User, on_delete=models.CASCADE)
12 | course = models.ForeignKey(Course, on_delete=models.CASCADE)
13 | title = models.TextField()
14 | content = RichTextField()
15 | start_time = models.DateTimeField()
16 | end_time = models.DateTimeField()
17 | create_time = models.DateTimeField(auto_now_add=True)
18 | last_update_time = models.DateTimeField(auto_now=True)
19 | visible = models.BooleanField(default=True)
20 |
21 | @property
22 | def status(self):
23 | if self.start_time > now():
24 | # NOT_START return 1
25 | return AssignmentStatus.ASSIGNMENT_NOT_START
26 | elif self.end_time < now():
27 | # ENDED return -1
28 | return AssignmentStatus.ASSIGNMENT_ENDED
29 | else:
30 | # UNDERWAY return 0
31 | return AssignmentStatus.ASSIGNMENT_UNDERWAY
32 |
33 | def problem_details_permission(self, user):
34 | return user.is_authenticated and user.is_assignment_admin(self)
35 |
36 | class Meta:
37 | db_table = "assignment"
38 | ordering = ("-start_time",)
39 |
--------------------------------------------------------------------------------
/backend/assignment/urls/professor.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from ..views.professor import AssignmentAPI, DownloadAssignmentSubmissions
4 |
5 | urlpatterns = [
6 | path("course/assignment/", AssignmentAPI.as_view(), name="assignment_professor_api"),
7 | path("download_submissions/", DownloadAssignmentSubmissions.as_view(), name="download_assignment_submissions"),
8 | ]
9 |
--------------------------------------------------------------------------------
/backend/assignment/urls/student.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from ..views.student import AssignmentAPI
4 |
5 | urlpatterns = [
6 | path("course/assignment/", AssignmentAPI.as_view(), name="assignment_api"),
7 | ]
8 |
--------------------------------------------------------------------------------
/backend/banner/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/banner/__init__.py
--------------------------------------------------------------------------------
/backend/banner/fixtures/data.yaml:
--------------------------------------------------------------------------------
1 | - model: banner.banner
2 | pk: 1
3 | fields:
4 | title: banner1.png
5 | create_time: 2022-02-17 10:48:49.019174+00:00
6 | path: /public/upload/banner1.png
7 | visible: true
8 |
9 | - model: banner.banner
10 | pk: 2
11 | fields:
12 | title: banner2.png
13 | create_time: 2022-02-17 10:48:49.019174+00:00
14 | path: /public/upload/banner2.png
15 | visible: true
16 |
17 | - model: banner.banner
18 | pk: 3
19 | fields:
20 | title: banner3.png
21 | create_time: 2022-02-17 10:48:49.019174+00:00
22 | path: /public/upload/banner3.png
23 | visible: true
24 |
25 | - model: banner.banner
26 | pk: 4
27 | fields:
28 | title: banner4.png
29 | create_time: 2022-02-17 10:48:49.019174+00:00
30 | path: /public/upload/banner4.png
31 | visible: true
32 |
--------------------------------------------------------------------------------
/backend/banner/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.9 on 2022-01-01 10:15
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | initial = True
9 |
10 | dependencies = [
11 | ]
12 |
13 | operations = [
14 | migrations.CreateModel(
15 | name='Banner',
16 | fields=[
17 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
18 | ('title', models.TextField()),
19 | ('create_time', models.DateTimeField(auto_now_add=True)),
20 | ('path', models.TextField()),
21 | ('visible', models.BooleanField(default=False)),
22 | ],
23 | options={
24 | 'db_table': 'banner',
25 | 'ordering': ('-create_time',),
26 | },
27 | ),
28 | ]
29 |
--------------------------------------------------------------------------------
/backend/banner/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/banner/migrations/__init__.py
--------------------------------------------------------------------------------
/backend/banner/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 |
4 | class Banner(models.Model):
5 | title = models.TextField()
6 | create_time = models.DateTimeField(auto_now_add=True)
7 | path = models.TextField()
8 | visible = models.BooleanField(default=False)
9 |
10 | class Meta:
11 | db_table = "banner"
12 | ordering = ("-create_time",)
13 |
--------------------------------------------------------------------------------
/backend/banner/serializers.py:
--------------------------------------------------------------------------------
1 | from utils.api import serializers
2 |
3 | from .models import Banner
4 |
5 |
6 | class BannerSerializer(serializers.Serializer):
7 | path = serializers.CharField(max_length=1024)
8 |
9 |
10 | class BannerAdminSerializer(serializers.ModelSerializer):
11 | class Meta:
12 | model = Banner
13 | fields = "__all__"
14 |
15 |
16 | class CreateBannerSerializer(serializers.Serializer):
17 | title = serializers.CharField(max_length=64)
18 | path = serializers.CharField(max_length=1024)
19 |
20 |
21 | class EditBannerSerializer(serializers.Serializer):
22 | id = serializers.IntegerField()
23 | path = serializers.CharField()
24 | visible = serializers.BooleanField()
25 |
--------------------------------------------------------------------------------
/backend/banner/tests.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/banner/tests.py
--------------------------------------------------------------------------------
/backend/banner/urls/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/banner/urls/__init__.py
--------------------------------------------------------------------------------
/backend/banner/urls/admin.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from ..views.admin import BannerAdminAPI
4 |
5 | urlpatterns = [
6 | path("banner/", BannerAdminAPI.as_view(), name="banner_admin_api")
7 | ]
8 |
--------------------------------------------------------------------------------
/backend/banner/urls/oj.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from ..views.oj import BannerAPI
4 |
5 | urlpatterns = [
6 | path("banner/", BannerAPI.as_view(), name="banner_api")
7 | ]
8 |
--------------------------------------------------------------------------------
/backend/banner/views/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/banner/views/__init__.py
--------------------------------------------------------------------------------
/backend/banner/views/oj.py:
--------------------------------------------------------------------------------
1 | from drf_yasg.utils import swagger_auto_schema
2 |
3 | from banner.models import Banner
4 | from banner.serializers import BannerSerializer
5 | from utils.api import APIView
6 |
7 |
8 | class BannerAPI(APIView):
9 | @swagger_auto_schema(
10 | manual_parameters=[],
11 | operation_description="Get Banner Image List"
12 | )
13 | def get(self, request):
14 | banners = Banner.objects.filter(visible=True)
15 | data = {}
16 | data["path"] = BannerSerializer(banners, many=True).data
17 | return self.success(data)
18 |
--------------------------------------------------------------------------------
/backend/conf/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/conf/__init__.py
--------------------------------------------------------------------------------
/backend/conf/migrations/0002_auto_20171011_1214.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.4 on 2017-10-11 12:14
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('conf', '0001_initial'),
12 | ]
13 |
14 | operations = [
15 | migrations.DeleteModel(
16 | name='JudgeServerToken',
17 | ),
18 | migrations.DeleteModel(
19 | name='SMTPConfig',
20 | ),
21 | migrations.DeleteModel(
22 | name='WebsiteConfig',
23 | ),
24 | migrations.AlterField(
25 | model_name='judgeserver',
26 | name='hostname',
27 | field=models.CharField(max_length=128),
28 | ),
29 | migrations.AlterField(
30 | model_name='judgeserver',
31 | name='judger_version',
32 | field=models.CharField(max_length=32),
33 | ),
34 | migrations.AlterField(
35 | model_name='judgeserver',
36 | name='service_url',
37 | field=models.CharField(blank=True, max_length=256, null=True),
38 | ),
39 | ]
40 |
--------------------------------------------------------------------------------
/backend/conf/migrations/0003_judgeserver_is_disabled.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.4 on 2017-12-24 03:44
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('conf', '0002_auto_20171011_1214'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='judgeserver',
17 | name='is_disabled',
18 | field=models.BooleanField(default=False),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/backend/conf/migrations/0004_auto_20180501_0436.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.3 on 2018-05-01 04:36
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('conf', '0003_judgeserver_is_disabled'),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterField(
16 | model_name='judgeserver',
17 | name='hostname',
18 | field=models.TextField(),
19 | ),
20 | migrations.AlterField(
21 | model_name='judgeserver',
22 | name='ip',
23 | field=models.TextField(null=True),
24 | ),
25 | migrations.AlterField(
26 | model_name='judgeserver',
27 | name='judger_version',
28 | field=models.TextField(),
29 | ),
30 | migrations.AlterField(
31 | model_name='judgeserver',
32 | name='service_url',
33 | field=models.TextField(null=True),
34 | ),
35 | ]
36 |
--------------------------------------------------------------------------------
/backend/conf/migrations/0005_alter_judgeserver_id.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.5 on 2021-12-26 05:58
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('conf', '0004_auto_20180501_0436'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='judgeserver',
15 | name='id',
16 | field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/conf/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/conf/migrations/__init__.py
--------------------------------------------------------------------------------
/backend/conf/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.utils import timezone
3 | from utils.shortcuts import get_env
4 |
5 |
6 | class JudgeServer(models.Model):
7 | hostname = models.TextField()
8 | ip = models.TextField(null=True)
9 | judger_version = models.TextField()
10 | cpu_core = models.IntegerField()
11 | memory_usage = models.FloatField()
12 | cpu_usage = models.FloatField()
13 | last_heartbeat = models.DateTimeField()
14 | create_time = models.DateTimeField(auto_now_add=True)
15 | task_number = models.IntegerField(default=0)
16 | service_url = models.TextField(null=True)
17 | is_disabled = models.BooleanField(default=False)
18 |
19 | @property
20 | def status(self):
21 | # In devcontainer, ignore heartbeat since server is not always running (PR#250)
22 | if get_env("DISABLE_HEARTBEAT"):
23 | return "normal"
24 | # Increase the one-second delay to improve adaptability to the network environment
25 | if (timezone.now() - self.last_heartbeat).total_seconds() > 6:
26 | return "abnormal"
27 | return "normal"
28 |
29 | class Meta:
30 | db_table = "judge_server"
31 |
--------------------------------------------------------------------------------
/backend/conf/urls/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/conf/urls/__init__.py
--------------------------------------------------------------------------------
/backend/conf/urls/admin.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from ..views import SMTPAPI, JudgeServerAPI, WebsiteConfigAPI, TestCasePruneAPI, SMTPTestAPI, IpAddressInfoAPI
4 | from ..views import ReleaseNotesAPI, DashboardInfoAPI
5 |
6 | urlpatterns = [
7 | path("smtp/", SMTPAPI.as_view(), name="smtp_admin_api"),
8 | path("smtp_test/", SMTPTestAPI.as_view(), name="smtp_test_api"),
9 | path("website/", WebsiteConfigAPI.as_view(), name="website_config_api"),
10 | path("judge_server/", JudgeServerAPI.as_view(), name="judge_server_api"),
11 | path("prune_test_case/", TestCasePruneAPI.as_view(), name="prune_test_case_api"),
12 | path("versions/", ReleaseNotesAPI.as_view(), name="get_release_notes_api"),
13 | path("dashboard_info/", DashboardInfoAPI.as_view(), name="dashboard_info_api"),
14 | path("ip_info/", IpAddressInfoAPI.as_view(), name="ip_address_info_api"),
15 | ]
16 |
--------------------------------------------------------------------------------
/backend/conf/urls/oj.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from ..views import JudgeServerHeartbeatAPI, LanguagesAPI, WebsiteConfigAPI
4 |
5 | urlpatterns = [
6 | path("website/", WebsiteConfigAPI.as_view(), name="website_info_api"),
7 | path("judge_server_heartbeat/", JudgeServerHeartbeatAPI.as_view(), name="judge_server_heartbeat_api"),
8 | path("languages/", LanguagesAPI.as_view(), name="language_list_api")
9 | ]
10 |
--------------------------------------------------------------------------------
/backend/conf/urls/professor.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from ..views import ProfessorDashboardInfoAPI
4 |
5 | urlpatterns = [
6 | path("professor_dashboard_info/", ProfessorDashboardInfoAPI.as_view(), name="professor_dashboard_info_api"),
7 | ]
8 |
--------------------------------------------------------------------------------
/backend/contest/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/contest/__init__.py
--------------------------------------------------------------------------------
/backend/contest/migrations/0002_auto_20170209_0845.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.12 on 2017-02-09 08:45
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('contest', '0001_initial'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='contestproblem',
17 | name='_id',
18 | field=models.CharField(db_index=True, default='1', max_length=24),
19 | preserve_default=False,
20 | ),
21 | migrations.RemoveField(
22 | model_name='contestproblem',
23 | name='sort_index',
24 | ),
25 | migrations.AlterUniqueTogether(
26 | name='contestproblem',
27 | unique_together=set([('_id', 'contest')]),
28 | ),
29 | ]
30 |
--------------------------------------------------------------------------------
/backend/contest/migrations/0003_auto_20170217_0820.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.12 on 2017-02-17 08:20
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('contest', '0002_auto_20170209_0845'),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterUniqueTogether(
16 | name='contestproblem',
17 | unique_together=set([]),
18 | ),
19 | migrations.RemoveField(
20 | model_name='contestproblem',
21 | name='contest',
22 | ),
23 | migrations.RemoveField(
24 | model_name='contestproblem',
25 | name='created_by',
26 | ),
27 | migrations.RemoveField(
28 | model_name='contestproblem',
29 | name='tags',
30 | ),
31 | migrations.DeleteModel(
32 | name='ContestProblem',
33 | ),
34 | ]
35 |
--------------------------------------------------------------------------------
/backend/contest/migrations/0004_auto_20170717_1324.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2017-07-17 13:24
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('contest', '0003_auto_20170217_0820'),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterModelOptions(
16 | name='contest',
17 | options={'ordering': ('-create_time',)},
18 | ),
19 | migrations.AlterModelOptions(
20 | name='contestannouncement',
21 | options={'ordering': ('-create_time',)},
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/backend/contest/migrations/0005_auto_20170823_0918.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2017-08-23 09:18
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('contest', '0004_auto_20170717_1324'),
12 | ]
13 |
14 | operations = [
15 | migrations.RenameField(
16 | model_name='acmcontestrank',
17 | old_name='total_ac_number',
18 | new_name='accepted_number',
19 | ),
20 | migrations.RenameField(
21 | model_name='acmcontestrank',
22 | old_name='total_submission_number',
23 | new_name='submission_number',
24 | ),
25 | migrations.RenameField(
26 | model_name='oicontestrank',
27 | old_name='total_submission_number',
28 | new_name='submission_number',
29 | ),
30 | ]
31 |
--------------------------------------------------------------------------------
/backend/contest/migrations/0006_auto_20171011_1214.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.4 on 2017-10-11 12:14
3 | from __future__ import unicode_literals
4 |
5 | import django.contrib.postgres.fields.jsonb
6 | from django.db import migrations
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | dependencies = [
12 | ('contest', '0005_auto_20170823_0918'),
13 | ]
14 |
15 | operations = [
16 | migrations.AlterField(
17 | model_name='acmcontestrank',
18 | name='submission_info',
19 | field=django.contrib.postgres.fields.jsonb.JSONField(default=dict),
20 | ),
21 | migrations.AlterField(
22 | model_name='oicontestrank',
23 | name='submission_info',
24 | field=django.contrib.postgres.fields.jsonb.JSONField(default=dict),
25 | ),
26 | migrations.AlterModelOptions(
27 | name='contest',
28 | options={'ordering': ('-start_time',)},
29 | ),
30 | ]
31 |
--------------------------------------------------------------------------------
/backend/contest/migrations/0007_contestannouncement_visible.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.4 on 2017-11-06 09:02
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('contest', '0006_auto_20171011_1214'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='contestannouncement',
17 | name='visible',
18 | field=models.BooleanField(default=True),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/backend/contest/migrations/0008_contest_allowed_ip_ranges.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.4 on 2017-11-10 06:57
3 | from __future__ import unicode_literals
4 |
5 | import django.contrib.postgres.fields.jsonb
6 | from django.db import migrations
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | dependencies = [
12 | ('contest', '0007_contestannouncement_visible'),
13 | ]
14 |
15 | operations = [
16 | migrations.AddField(
17 | model_name='contest',
18 | name='allowed_ip_ranges',
19 | field=django.contrib.postgres.fields.jsonb.JSONField(default=list),
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/backend/contest/migrations/0009_auto_20180501_0436.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.3 on 2018-05-01 04:36
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('contest', '0008_contest_allowed_ip_ranges'),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterField(
16 | model_name='contest',
17 | name='password',
18 | field=models.TextField(null=True),
19 | ),
20 | migrations.AlterField(
21 | model_name='contest',
22 | name='rule_type',
23 | field=models.TextField(),
24 | ),
25 | migrations.AlterField(
26 | model_name='contest',
27 | name='title',
28 | field=models.TextField(),
29 | ),
30 | migrations.AlterField(
31 | model_name='contestannouncement',
32 | name='title',
33 | field=models.TextField(),
34 | ),
35 | ]
36 |
--------------------------------------------------------------------------------
/backend/contest/migrations/0010_auto_20190326_0201.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.7 on 2019-03-26 02:01
2 |
3 | from django.conf import settings
4 | from django.db import migrations
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
11 | ('contest', '0009_auto_20180501_0436'),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterUniqueTogether(
16 | name='acmcontestrank',
17 | unique_together={('user', 'contest')},
18 | ),
19 | migrations.AlterUniqueTogether(
20 | name='oicontestrank',
21 | unique_together={('user', 'contest')},
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/backend/contest/migrations/0012_rename_total_score_acmcontestrank_total_penalty.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.4 on 2021-11-04 12:42
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('contest', '0011_auto_20211030_1535'),
10 | ]
11 |
12 | operations = [
13 | migrations.RenameField(
14 | model_name='acmcontestrank',
15 | old_name='total_score',
16 | new_name='total_penalty',
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/contest/migrations/0013_contestannouncement_problem.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.10 on 2022-01-07 14:16
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('problem', '__first__'),
11 | ('contest', '0012_rename_total_score_acmcontestrank_total_penalty'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='contestannouncement',
17 | name='problem',
18 | field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='problem.problem'),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/backend/contest/migrations/0014_auto_20220211_1432.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.12 on 2022-02-11 05:32
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('group', '0004_rename_is_admin_groupmember_is_group_admin'),
10 | ('contest', '0013_contestannouncement_problem'),
11 | ]
12 |
13 | operations = [
14 | migrations.AddField(
15 | model_name='contest',
16 | name='allowed_groups',
17 | field=models.ManyToManyField(to='group.Group'),
18 | ),
19 | migrations.AddField(
20 | model_name='contest',
21 | name='prize',
22 | field=models.JSONField(default=dict),
23 | ),
24 | migrations.AddField(
25 | model_name='contest',
26 | name='scoring',
27 | field=models.TextField(default='ACM-ICPC style'),
28 | ),
29 | ]
30 |
--------------------------------------------------------------------------------
/backend/contest/migrations/0015_auto_20220211_1433.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.12 on 2022-02-11 05:33
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('contest', '0014_auto_20220211_1432'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='contest',
15 | name='constraints',
16 | field=models.JSONField(default=list),
17 | ),
18 | migrations.AddField(
19 | model_name='contest',
20 | name='requirements',
21 | field=models.JSONField(default=list),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/backend/contest/migrations/0016_rename_prize_contest_prizes.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.12 on 2022-02-11 05:52
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('contest', '0015_auto_20220211_1433'),
10 | ]
11 |
12 | operations = [
13 | migrations.RenameField(
14 | model_name='contest',
15 | old_name='prize',
16 | new_name='prizes',
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/contest/migrations/0017_alter_contest_allowed_groups.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.12 on 2022-02-11 05:56
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('group', '0004_rename_is_admin_groupmember_is_group_admin'),
10 | ('contest', '0016_rename_prize_contest_prizes'),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name='contest',
16 | name='allowed_groups',
17 | field=models.ManyToManyField(blank=True, to='group.Group'),
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/backend/contest/migrations/0018_auto_20220225_1422.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.12 on 2022-02-25 05:22
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('contest', '0017_alter_contest_allowed_groups'),
11 | ]
12 |
13 | operations = [
14 | migrations.RemoveField(
15 | model_name='contest',
16 | name='prizes',
17 | ),
18 | migrations.CreateModel(
19 | name='ContestPrize',
20 | fields=[
21 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
22 | ('color', models.TextField()),
23 | ('name', models.TextField()),
24 | ('reward', models.TextField()),
25 | ('contest', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contest.contest')),
26 | ],
27 | ),
28 | migrations.AddField(
29 | model_name='acmcontestrank',
30 | name='prize',
31 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='contest.contestprize'),
32 | ),
33 | migrations.AddField(
34 | model_name='oicontestrank',
35 | name='prize',
36 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='contest.contestprize'),
37 | ),
38 | ]
39 |
--------------------------------------------------------------------------------
/backend/contest/migrations/0019_alter_contestprize_contest.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.12 on 2022-02-25 05:46
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('contest', '0018_auto_20220225_1422'),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name='contestprize',
16 | name='contest',
17 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='prizes', to='contest.contest'),
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/backend/contest/migrations/0020_auto_20220315_1654.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.12 on 2022-03-15 07:54
2 |
3 | from django.conf import settings
4 | from django.db import migrations, models
5 | import django.db.models.deletion
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
12 | ('contest', '0019_alter_contestprize_contest'),
13 | ]
14 |
15 | operations = [
16 | migrations.AddField(
17 | model_name='contest',
18 | name='bank_filter',
19 | field=models.JSONField(default=None, null=True),
20 | ),
21 | migrations.CreateModel(
22 | name='ProblemBank',
23 | fields=[
24 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
25 | ('problem_list', models.TextField(null=True)),
26 | ('contest', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contest.contest')),
27 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
28 | ],
29 | options={
30 | 'db_table': 'problem_bank',
31 | 'unique_together': {('user', 'contest')},
32 | },
33 | ),
34 | ]
35 |
--------------------------------------------------------------------------------
/backend/contest/migrations/0021_contest_rank_penalty_visible.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.12 on 2022-04-10 08:15
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('contest', '0020_auto_20220315_1654'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='contest',
15 | name='rank_penalty_visible',
16 | field=models.BooleanField(default=True),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/contest/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/contest/migrations/__init__.py
--------------------------------------------------------------------------------
/backend/contest/urls/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/contest/urls/__init__.py
--------------------------------------------------------------------------------
/backend/contest/urls/admin.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from ..views.admin import ContestAnnouncementAPI, ContestAPI, ContestRankAPI, DownloadContestSubmissions
4 |
5 | urlpatterns = [
6 | path("contest/", ContestAPI.as_view(), name="contest_admin_api"),
7 | path("contest/announcement/", ContestAnnouncementAPI.as_view(), name="contest_announcement_admin_api"),
8 | path("download_submissions/", DownloadContestSubmissions.as_view(), name="acm_contest_helper"),
9 | path("contest/rank/", ContestRankAPI.as_view(), name="contest_rank_api")
10 | ]
11 |
--------------------------------------------------------------------------------
/backend/contest/urls/oj.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from ..views.oj import ContestAnnouncementListAPI
4 | from ..views.oj import ContestPasswordVerifyAPI, ContestAccessAPI
5 | from ..views.oj import ContestListAPI, ContestAPI, ContestRankAPI, ProblemBankAPI
6 |
7 | urlpatterns = [
8 | path("contests/", ContestListAPI.as_view(), name="contest_list_api"),
9 | path("contest/", ContestAPI.as_view(), name="contest_api"),
10 | path("contest/password/", ContestPasswordVerifyAPI.as_view(), name="contest_password_api"),
11 | path("contest/announcement/", ContestAnnouncementListAPI.as_view(), name="contest_announcement_api"),
12 | path("contest/access/", ContestAccessAPI.as_view(), name="contest_access_api"),
13 | path("contest/rank/", ContestRankAPI.as_view(), name="contest_rank_api"),
14 | path("contest/bank/", ProblemBankAPI.as_view(), name="contest_bank_api"),
15 | ]
16 |
--------------------------------------------------------------------------------
/backend/contest/views/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/contest/views/__init__.py
--------------------------------------------------------------------------------
/backend/course/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/course/__init__.py
--------------------------------------------------------------------------------
/backend/course/migrations/0002_auto_20210808_1709.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.24 on 2021-08-08 08:09
2 |
3 | from django.conf import settings
4 | from django.db import migrations
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
11 | ('course', '0001_initial'),
12 | ]
13 |
14 | operations = [
15 | migrations.RenameModel(
16 | old_name='Takes',
17 | new_name='Registration',
18 | ),
19 | migrations.AlterModelTable(
20 | name='registration',
21 | table='registration',
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/backend/course/migrations/0003_auto_20211226_1458.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.5 on 2021-12-26 05:58
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('course', '0002_auto_20210808_1709'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='course',
15 | name='id',
16 | field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
17 | ),
18 | migrations.AlterField(
19 | model_name='registration',
20 | name='id',
21 | field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/backend/course/migrations/0004_registration_bookmark.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.5 on 2021-12-26 07:28
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('course', '0003_auto_20211226_1458'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='registration',
15 | name='bookmark',
16 | field=models.BooleanField(default=True),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/course/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/course/migrations/__init__.py
--------------------------------------------------------------------------------
/backend/course/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | from account.models import User
4 |
5 |
6 | class Course(models.Model):
7 | created_by = models.ForeignKey(User, on_delete=models.CASCADE)
8 | title = models.TextField()
9 | course_code = models.TextField()
10 | class_number = models.IntegerField()
11 | registered_year = models.IntegerField()
12 | semester = models.IntegerField()
13 |
14 | class Meta:
15 | db_table = "course"
16 | ordering = ("-registered_year",)
17 |
18 |
19 | class Registration(models.Model):
20 | course = models.ForeignKey(Course, on_delete=models.CASCADE)
21 | user = models.ForeignKey(User, on_delete=models.CASCADE)
22 | bookmark = models.BooleanField(default=True)
23 |
24 | class Meta:
25 | db_table = "registration"
26 |
--------------------------------------------------------------------------------
/backend/course/urls/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/course/urls/__init__.py
--------------------------------------------------------------------------------
/backend/course/urls/professor.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from ..views.professor import BookmarkCourseAPI, CourseAPI, StudentManagementAPI
4 |
5 | urlpatterns = [
6 | path("course/", CourseAPI.as_view(), name="course_professor_api"),
7 | path("course/students/", StudentManagementAPI.as_view(), name="student_management_api"),
8 | path("bookmark_course/", BookmarkCourseAPI.as_view(), name="bookmark_professor_api")
9 | ]
10 |
--------------------------------------------------------------------------------
/backend/course/urls/student.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from ..views.student import BookmarkCourseAPI, CourseAPI
4 |
5 | urlpatterns = [
6 | path("course/", CourseAPI.as_view(), name="course_api"),
7 | path("bookmark_course/", BookmarkCourseAPI.as_view(), name="bookmark_course_api"),
8 | ]
9 |
--------------------------------------------------------------------------------
/backend/course/views/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/course/views/__init__.py
--------------------------------------------------------------------------------
/backend/data/.gitignore:
--------------------------------------------------------------------------------
1 | **/*
2 | !.gitignore
3 | !config/
4 | !config/.gitkeep
5 | !log/
6 | !log/.gitkeep
7 | !public/
8 | !public/avatar/
9 | !public/avatar/default.png
10 | !public/upload/
11 | !public/upload/banner[1234].png
12 | !ssl/
13 | !ssl/.gitkeep
14 | !test_case/
15 | !test_case/.gitkeep
16 |
--------------------------------------------------------------------------------
/backend/data/config/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/data/config/.gitkeep
--------------------------------------------------------------------------------
/backend/data/log/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/data/log/.gitkeep
--------------------------------------------------------------------------------
/backend/data/public/avatar/default.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/data/public/avatar/default.png
--------------------------------------------------------------------------------
/backend/data/public/upload/banner1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/data/public/upload/banner1.png
--------------------------------------------------------------------------------
/backend/data/public/upload/banner2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/data/public/upload/banner2.png
--------------------------------------------------------------------------------
/backend/data/public/upload/banner3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/data/public/upload/banner3.png
--------------------------------------------------------------------------------
/backend/data/public/upload/banner4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/data/public/upload/banner4.png
--------------------------------------------------------------------------------
/backend/data/public/website/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/data/public/website/favicon.ico
--------------------------------------------------------------------------------
/backend/data/ssl/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/data/ssl/.gitkeep
--------------------------------------------------------------------------------
/backend/data/test_case/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/data/test_case/.gitkeep
--------------------------------------------------------------------------------
/backend/deploy/health_check.py:
--------------------------------------------------------------------------------
1 | from xmlrpc.client import Server
2 |
3 | if __name__ == "__main__":
4 | try:
5 | server = Server("http://localhost:9005/RPC2")
6 | info = server.supervisor.getAllProcessInfo()
7 | error_states = list(filter(lambda x: x["state"] != 20, info))
8 | exit(len(error_states))
9 | except Exception as e:
10 | print(e.with_traceback())
11 | exit(1)
12 |
--------------------------------------------------------------------------------
/backend/deploy/nginx/api_proxy.conf:
--------------------------------------------------------------------------------
1 | proxy_pass http://backend;
2 | proxy_set_header X-Real-IP __IP_HEADER__;
3 | proxy_set_header Host $http_host;
4 | client_max_body_size 200M;
5 | proxy_http_version 1.1;
6 | proxy_set_header Connection '';
--------------------------------------------------------------------------------
/backend/deploy/nginx/locations.conf:
--------------------------------------------------------------------------------
1 | location /public {
2 | root /data;
3 | }
4 |
5 | location /api {
6 | include api_proxy.conf;
7 | }
8 |
9 | location /admin {
10 | root /app/dist/admin;
11 | try_files $uri $uri/ /index.html =404;
12 | }
13 |
14 | location /professor {
15 | root /app/dist/professor;
16 | try_files $uri $uri/ /index.html =404;
17 | }
18 |
19 | location /.well-known {
20 | alias /data/ssl/.well-known;
21 | }
22 |
23 | location / {
24 | root /app/dist;
25 | try_files $uri $uri/ /index.html =404;
26 | }
27 |
--------------------------------------------------------------------------------
/backend/deploy/requirements.txt:
--------------------------------------------------------------------------------
1 | asgiref==3.4.1
2 | attrs==20.3.0
3 | certifi==2020.12.5
4 | chardet==4.0.0
5 | click==8.0.1
6 | coreapi==2.3.3
7 | coreschema==0.0.4
8 | coverage==5.4
9 | Django==3.2.12
10 | django-dbconn-retry==0.1.5
11 | django-dramatiq==0.10.0
12 | django-redis==4.12.1
13 | djangorestframework==3.12.2
14 | dramatiq==1.10.0
15 | drf-yasg==1.20.0
16 | entrypoints==0.3
17 | Envelopes==0.4
18 | flake8==4.0.1
19 | flake8-coding==1.3.2
20 | flake8-quotes==3.2.0
21 | gunicorn==20.0.4
22 | h11==0.12.0
23 | idna==2.10
24 | inflection==0.5.1
25 | itypes==1.2.0
26 | Jinja2==2.11.3
27 | jsonfield==3.1.0
28 | jsonschema==3.2.0
29 | MarkupSafe==1.1.1
30 | mccabe==0.6.1
31 | packaging==20.9
32 | Pillow==9.0.1
33 | prometheus-client==0.9.0
34 | psycopg2-binary==2.8.6
35 | pycodestyle==2.8.0
36 | pyflakes==2.4.0
37 | pyparsing==2.4.7
38 | pyrsistent==0.17.3
39 | python-dateutil==2.8.1
40 | pytz==2020.5
41 | PyYAML==5.4.1
42 | redis==3.5.3
43 | requests==2.25.1
44 | ruamel.yaml==0.16.12
45 | ruamel.yaml.clib==0.2.2
46 | six==1.15.0
47 | sqlparse==0.4.2
48 | uritemplate==3.0.1
49 | urllib3==1.26.5
50 | uvicorn==0.15.0
51 | XlsxWriter==1.3.7
52 |
--------------------------------------------------------------------------------
/backend/deploy/supervisord.conf:
--------------------------------------------------------------------------------
1 | [supervisord]
2 | logfile=/data/log/supervisord.log
3 | logfile_maxbytes=10MB
4 | logfile_backups=10
5 | loglevel=info
6 | pidfile=/tmp/supervisord.pid
7 | nodaemon=true
8 | childlogdir=/data/log/
9 |
10 | [inet_http_server]
11 | port=127.0.0.1:9005
12 |
13 | [rpcinterface:supervisor]
14 | supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface
15 |
16 | [supervisorctl]
17 | serverurl=http://127.0.0.1:9005
18 |
19 | [program:nginx]
20 | command=nginx -c /app/deploy/nginx/nginx.conf
21 | directory=/app/
22 | stdout_logfile=/data/log/nginx.log
23 | stderr_logfile=/data/log/nginx.log
24 | autostart=true
25 | autorestart=true
26 | startsecs=5
27 | stopwaitsecs=5
28 | killasgroup=true
29 |
30 | [program:gunicorn]
31 | command=gunicorn oj.asgi --user server --group spj --bind 127.0.0.1:8080 --workers %(ENV_MAX_WORKER_NUM)s --worker-class uvicorn.workers.UvicornWorker --threads 4 --max-requests-jitter 10000 --max-requests 1000000 --keep-alive 32
32 | directory=/app/
33 | stdout_logfile=/data/log/gunicorn.log
34 | stderr_logfile=/data/log/gunicorn.log
35 | autostart=true
36 | autorestart=true
37 | startsecs=5
38 | stopwaitsecs=5
39 | killasgroup=true
40 |
41 | [program:dramatiq]
42 | command=python3 manage.py rundramatiq --processes %(ENV_MAX_WORKER_NUM)s --threads 4
43 | directory=/app/
44 | user=nobody
45 | stdout_logfile=/data/log/dramatiq.log
46 | stderr_logfile=/data/log/dramatiq.log
47 | autostart=true
48 | autorestart=true
49 | startsecs=5
50 | stopwaitsecs=5
51 | killasgroup=true
52 |
--------------------------------------------------------------------------------
/backend/deploy/test_case_rsync/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM alpine:3.6
2 |
3 | RUN apk add --update --no-cache rsync
4 |
5 | ADD ./run.sh /tmp/run.sh
6 | ADD ./rsyncd.conf /etc/rsyncd.conf
7 |
8 | CMD /bin/sh /tmp/run.sh
--------------------------------------------------------------------------------
/backend/deploy/test_case_rsync/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3"
2 | services:
3 | oj-rsync-master:
4 | image: oj_rsync
5 | container_name: oj-rsync
6 | volumes:
7 | - $PWD/data/backend/test_case:/test_case:ro
8 | - $PWD/data/rsync_master:/log
9 | environment:
10 | - RSYNC_MODE=master
11 | - RSYNC_USER=ojrsync
12 | - RSYNC_PASSWORD=CHANGE_THIS_PASSWORD
13 | ports:
14 | - "0.0.0.0:873:873"
15 |
16 | oj-rsync-slave:
17 | image: oj-rsync
18 | volumes:
19 | - $PWD/test_case:/test_case
20 | - $PWD/rsync_slave:/log
21 | environment:
22 | - RSYNC_MODE=slave
23 | - RSYNC_USER=ojrsync
24 | - RSYNC_PASSWORD=CHANGE_THIS_PASSWORD
25 |
--------------------------------------------------------------------------------
/backend/deploy/test_case_rsync/rsyncd.conf:
--------------------------------------------------------------------------------
1 | port = 873
2 | uid = root
3 | gid = root
4 | use chroot = yes
5 | read only = yes
6 | log file = /log/rsyncd.log
7 |
8 | [testcase]
9 | path = /test_case/
10 | list = yes
11 | auth users = ojrsync
12 | secrets file = /etc/rsyncd.passwd
13 |
--------------------------------------------------------------------------------
/backend/deploy/test_case_rsync/run.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | slave_runner()
4 | {
5 | while true
6 | do
7 | rsync -avzP --delete --progress --password-file=/etc/rsync_slave.passwd $RSYNC_USER@$RSYNC_MASTER_ADDR::testcase /test_case >> /log/rsync_slave.log
8 | sleep 5
9 | done
10 | }
11 |
12 | master_runner()
13 | {
14 | rsync --daemon --config=/etc/rsyncd.conf
15 | while true
16 | do
17 | sleep 60
18 | done
19 | }
20 |
21 | if [ "$RSYNC_MODE" = "master" ]; then
22 | if [ ! -f "/etc/rsyncd.passwd" ]; then
23 | echo "$RSYNC_USER:$RSYNC_PASSWORD" > /etc/rsyncd.passwd
24 | fi
25 | chmod 600 /etc/rsyncd.passwd
26 | master_runner
27 | else
28 | if [ ! -f "/etc/rsync_slave.passwd" ]; then
29 | echo "$RSYNC_PASSWORD" > /etc/rsync_slave.passwd
30 | fi
31 | chmod 600 /etc/rsync_slave.passwd
32 | slave_runner
33 | fi
34 |
--------------------------------------------------------------------------------
/backend/group/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/group/__init__.py
--------------------------------------------------------------------------------
/backend/group/migrations/0002_alter_groupmember_is_admin.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.11 on 2022-02-02 17:18
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('group', '0001_initial'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='groupmember',
15 | name='is_admin',
16 | field=models.BooleanField(default=False),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/group/migrations/0003_auto_20220203_2234.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.11 on 2022-02-03 13:34
2 |
3 | from django.conf import settings
4 | from django.db import migrations
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | migrations.swappable_dependency(settings.AUTH_USER_MODEL),
11 | ('group', '0002_alter_groupmember_is_admin'),
12 | ]
13 |
14 | operations = [
15 | migrations.RenameModel(
16 | old_name='GroupApplication',
17 | new_name='GroupMemberJoin',
18 | ),
19 | migrations.AlterModelTable(
20 | name='groupmemberjoin',
21 | table='group_member_join',
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/backend/group/migrations/0004_rename_is_admin_groupmember_is_group_admin.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.11 on 2022-02-05 01:56
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('group', '0003_auto_20220203_2234'),
10 | ]
11 |
12 | operations = [
13 | migrations.RenameField(
14 | model_name='groupmember',
15 | old_name='is_admin',
16 | new_name='is_group_admin',
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/group/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/group/migrations/__init__.py
--------------------------------------------------------------------------------
/backend/group/models.py:
--------------------------------------------------------------------------------
1 | # from django.forms import ImageField
2 | from django.db import models
3 |
4 | from account.models import User
5 | from utils.models import RichTextField
6 |
7 |
8 | class GroupRegistrationRequest(models.Model):
9 | name = models.TextField()
10 | short_description = models.TextField()
11 | description = RichTextField()
12 | is_official = models.BooleanField()
13 |
14 | create_time = models.DateTimeField(auto_now_add=True)
15 | created_by = models.ForeignKey(User, on_delete=models.CASCADE)
16 |
17 | class Meta:
18 | db_table = "group_registration_request"
19 |
20 |
21 | class Group(models.Model):
22 | name = models.TextField()
23 | short_description = models.TextField()
24 | description = RichTextField()
25 | is_official = models.BooleanField()
26 | # logo = ImageField()
27 |
28 | created_by = models.ForeignKey(User, on_delete=models.PROTECT)
29 |
30 | create_time = models.DateTimeField(auto_now_add=True)
31 | last_update_time = models.DateTimeField(auto_now=True)
32 |
33 | members = models.ManyToManyField(User, related_name="groups", through="GroupMember", through_fields=("group", "user"))
34 |
35 | class Meta:
36 | db_table = "group"
37 |
38 |
39 | class GroupMember(models.Model):
40 | user = models.ForeignKey(User, on_delete=models.CASCADE)
41 | group = models.ForeignKey(Group, on_delete=models.CASCADE)
42 |
43 | is_group_admin = models.BooleanField(default=False)
44 |
45 |
46 | class GroupMemberJoin(models.Model):
47 | group = models.ForeignKey(Group, on_delete=models.CASCADE)
48 | created_by = models.ForeignKey(User, on_delete=models.CASCADE)
49 | description = RichTextField()
50 |
51 | class Meta:
52 | db_table = "group_member_join"
53 |
--------------------------------------------------------------------------------
/backend/group/urls/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/group/urls/__init__.py
--------------------------------------------------------------------------------
/backend/group/urls/admin.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 | from ..views.admin import AdminGroupRegistrationRequestAPI, GroupAdminAPI
3 |
4 |
5 | urlpatterns = [
6 | path("group/registration_request/", AdminGroupRegistrationRequestAPI.as_view(), name="group_registration_request_admin_api"),
7 | path("group/", GroupAdminAPI.as_view(), name="group_admin_api")
8 | ]
9 |
--------------------------------------------------------------------------------
/backend/group/urls/oj.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 | from ..views.oj import GroupMemberJoinAPI, GroupMemberAPI, GroupRegistrationRequestAPI, GroupAPI
3 |
4 |
5 | urlpatterns = [
6 | path("group/registration_request/", GroupRegistrationRequestAPI.as_view(), name="group_registration_request_api"),
7 | path("group/", GroupAPI.as_view(), name="group_api"),
8 | path("group/member_join/", GroupMemberJoinAPI.as_view(), name="group_member_join_api"),
9 | path("group/member/", GroupMemberAPI.as_view(), name="group_member_api")
10 | ]
11 |
--------------------------------------------------------------------------------
/backend/group/views/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/group/views/__init__.py
--------------------------------------------------------------------------------
/backend/judge/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/judge/__init__.py
--------------------------------------------------------------------------------
/backend/judge/tasks.py:
--------------------------------------------------------------------------------
1 | import dramatiq
2 |
3 | from account.models import User
4 | from submission.models import Submission
5 | from judge.dispatcher import JudgeDispatcher
6 | from utils.shortcuts import DRAMATIQ_WORKER_ARGS
7 |
8 |
9 | @dramatiq.actor(**DRAMATIQ_WORKER_ARGS())
10 | def judge_task(submission_id, problem_id):
11 | uid = Submission.objects.get(id=submission_id).user_id
12 | if User.objects.get(id=uid).is_disabled:
13 | return
14 | JudgeDispatcher(submission_id, problem_id).judge()
15 |
--------------------------------------------------------------------------------
/backend/manage.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import os
3 | import sys
4 |
5 | if __name__ == "__main__":
6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "oj.settings")
7 |
8 | from django.core.management import execute_from_command_line
9 | import django
10 | sys.stdout.write("Django VERSION " + str(django.VERSION) + "\n")
11 |
12 | execute_from_command_line(sys.argv)
13 |
--------------------------------------------------------------------------------
/backend/oj/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/oj/__init__.py
--------------------------------------------------------------------------------
/backend/oj/asgi.py:
--------------------------------------------------------------------------------
1 | """
2 | ASGI config for oj project.
3 |
4 | It exposes the ASGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.asgi import get_asgi_application
13 |
14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "oj.settings")
15 |
16 | application = get_asgi_application()
17 |
--------------------------------------------------------------------------------
/backend/oj/dev_settings.py:
--------------------------------------------------------------------------------
1 | import os
2 | from utils.shortcuts import get_env
3 |
4 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
5 |
6 | DATABASES = {
7 | 'default': {
8 | 'ENGINE': 'django.db.backends.postgresql',
9 | 'HOST': get_env('POSTGRES_HOST', '127.0.0.1'),
10 | 'PORT': get_env('POSTGRES_PORT', '5432'),
11 | 'NAME': "codingplatform",
12 | 'USER': "codingplatform",
13 | 'PASSWORD': 'codingplatform'
14 | }
15 | }
16 |
17 | REDIS_CONF = {
18 | "host": get_env('REDIS_HOST', "127.0.0.1"),
19 | "port": get_env('REDIS_PORT', "6379")
20 | }
21 |
22 |
23 | DEBUG = True
24 |
25 | ALLOWED_HOSTS = ["*"]
26 |
27 | DATA_DIR = f"{BASE_DIR}/data"
28 |
--------------------------------------------------------------------------------
/backend/oj/production_settings.py:
--------------------------------------------------------------------------------
1 | from utils.shortcuts import get_env
2 |
3 | DATABASES = {
4 | 'default': {
5 | 'ENGINE': 'django.db.backends.postgresql',
6 | 'HOST': get_env("POSTGRES_HOST", "oj-postgres"),
7 | 'PORT': get_env("POSTGRES_PORT", "5432"),
8 | 'NAME': get_env("POSTGRES_DB"),
9 | 'USER': get_env("POSTGRES_USER"),
10 | 'PASSWORD': get_env("POSTGRES_PASSWORD")
11 | }
12 | }
13 |
14 | REDIS_CONF = {
15 | "host": get_env("REDIS_HOST", "oj-redis"),
16 | "port": get_env("REDIS_PORT", "6379")
17 | }
18 |
19 | DEBUG = False
20 |
21 | ALLOWED_HOSTS = ['*']
22 |
23 | DATA_DIR = "/data"
24 |
--------------------------------------------------------------------------------
/backend/oj/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import include, path
2 |
3 |
4 | urlpatterns = [
5 | path("api/", include("account.urls.oj")),
6 | path("api/admin/", include("account.urls.admin")),
7 | path("api/", include("announcement.urls.oj")),
8 | path("api/admin/", include("announcement.urls.admin")),
9 | path("api/", include("conf.urls.oj")),
10 | path("api/admin/", include("conf.urls.admin")),
11 | path("api/professor/", include("conf.urls.professor")),
12 | path("api/", include("problem.urls.oj")),
13 | path("api/admin/", include("problem.urls.admin")),
14 | path("api/lecture/", include("problem.urls.student")),
15 | path("api/lecture/professor/", include("problem.urls.professor")),
16 | path("api/", include("contest.urls.oj")),
17 | path("api/admin/", include("contest.urls.admin")),
18 | path("api/", include("submission.urls")),
19 | path("api/admin/", include("utils.urls")),
20 | path("api/", include("banner.urls.oj")),
21 | path("api/admin/", include("banner.urls.admin")),
22 | path("api/", include("group.urls.oj")),
23 | path("api/admin/", include("group.urls.admin")),
24 | path("api/lecture/", include("course.urls.student")),
25 | path("api/lecture/professor/", include("course.urls.professor")),
26 | path("api/lecture/", include("assignment.urls.student")),
27 | path("api/lecture/professor/", include("assignment.urls.professor")),
28 | path("api/lecture/", include("qna.urls.student")),
29 | path("api/lecture/professor/", include("qna.urls.professor")),
30 | ]
31 |
--------------------------------------------------------------------------------
/backend/oj/wsgi.py:
--------------------------------------------------------------------------------
1 | """
2 | WSGI config for qduoj project.
3 |
4 | It exposes the WSGI callable as a module-level variable named ``application``.
5 |
6 | For more information on this file, see
7 | https://docs.djangoproject.com/en/1.8/howto/deployment/wsgi/
8 | """
9 |
10 | import os
11 |
12 | from django.core.wsgi import get_wsgi_application
13 |
14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "oj.settings")
15 |
16 | application = get_wsgi_application()
17 |
--------------------------------------------------------------------------------
/backend/options/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/options/__init__.py
--------------------------------------------------------------------------------
/backend/options/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.4 on 2017-10-23 08:11
3 | from __future__ import unicode_literals
4 |
5 | import django.contrib.postgres.fields.jsonb
6 | from django.db import migrations, models
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | initial = True
12 |
13 | dependencies = [
14 | ]
15 |
16 | operations = [
17 | migrations.CreateModel(
18 | name='SysOptions',
19 | fields=[
20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
21 | ('key', models.CharField(db_index=True, max_length=128, unique=True)),
22 | ('value', django.contrib.postgres.fields.jsonb.JSONField()),
23 | ],
24 | ),
25 | ]
26 |
--------------------------------------------------------------------------------
/backend/options/migrations/0002_auto_20180501_0436.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.3 on 2018-05-01 04:36
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('options', '0001_initial'),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterField(
16 | model_name='sysoptions',
17 | name='key',
18 | field=models.TextField(db_index=True, unique=True),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/backend/options/migrations/0003_migrate_languages_options.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.3 on 2018-05-01 04:36
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('options', '0002_auto_20180501_0436'),
12 | ]
13 |
14 | operations = [
15 | migrations.RunSQL("""
16 | DELETE FROM options_sysoptions WHERE key = 'languages';
17 | """)
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/options/migrations/0004_auto_20211030_1535.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.4 on 2021-10-30 06:35
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('options', '0003_migrate_languages_options'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='sysoptions',
15 | name='id',
16 | field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'),
17 | ),
18 | migrations.AlterField(
19 | model_name='sysoptions',
20 | name='value',
21 | field=models.JSONField(),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/backend/options/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/options/migrations/__init__.py
--------------------------------------------------------------------------------
/backend/options/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 | from django.db.models import JSONField
3 |
4 |
5 | class SysOptions(models.Model):
6 | key = models.TextField(unique=True, db_index=True)
7 | value = JSONField()
8 |
--------------------------------------------------------------------------------
/backend/options/tests.py:
--------------------------------------------------------------------------------
1 | # Create your tests here.
2 |
--------------------------------------------------------------------------------
/backend/options/views.py:
--------------------------------------------------------------------------------
1 | # Create your views here.
2 |
--------------------------------------------------------------------------------
/backend/problem/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/problem/__init__.py
--------------------------------------------------------------------------------
/backend/problem/migrations/0002_problem__id.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.12 on 2017-02-09 08:45
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('problem', '0001_initial'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='problem',
17 | name='_id',
18 | field=models.CharField(db_index=True, default='1', max_length=24, unique=True),
19 | preserve_default=False,
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/backend/problem/migrations/0004_auto_20170501_0637.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2017-05-01 06:37
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('problem', '0003_auto_20170217_0820'),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterField(
16 | model_name='contestproblem',
17 | name='total_accepted_number',
18 | field=models.BigIntegerField(default=0),
19 | ),
20 | migrations.AlterField(
21 | model_name='contestproblem',
22 | name='total_submit_number',
23 | field=models.BigIntegerField(default=0),
24 | ),
25 | migrations.AlterField(
26 | model_name='problem',
27 | name='total_accepted_number',
28 | field=models.BigIntegerField(default=0),
29 | ),
30 | migrations.AlterField(
31 | model_name='problem',
32 | name='total_submit_number',
33 | field=models.BigIntegerField(default=0),
34 | ),
35 | ]
36 |
--------------------------------------------------------------------------------
/backend/problem/migrations/0005_auto_20170815_1258.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2017-08-15 12:58
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations
6 | import jsonfield.fields
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | dependencies = [
12 | ('problem', '0004_auto_20170501_0637'),
13 | ]
14 |
15 | operations = [
16 | migrations.AddField(
17 | model_name='contestproblem',
18 | name='statistic_info',
19 | field=jsonfield.fields.JSONField(default={}),
20 | ),
21 | migrations.AddField(
22 | model_name='problem',
23 | name='statistic_info',
24 | field=jsonfield.fields.JSONField(default={}),
25 | ),
26 | ]
27 |
--------------------------------------------------------------------------------
/backend/problem/migrations/0006_auto_20170823_0918.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2017-08-23 09:18
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('problem', '0005_auto_20170815_1258'),
12 | ]
13 |
14 | operations = [
15 | migrations.RenameField(
16 | model_name='contestproblem',
17 | old_name='total_accepted_number',
18 | new_name='accepted_number',
19 | ),
20 | migrations.RenameField(
21 | model_name='contestproblem',
22 | old_name='total_submit_number',
23 | new_name='submission_number',
24 | ),
25 | migrations.RenameField(
26 | model_name='problem',
27 | old_name='total_accepted_number',
28 | new_name='accepted_number',
29 | ),
30 | migrations.RenameField(
31 | model_name='problem',
32 | old_name='total_submit_number',
33 | new_name='submission_number',
34 | ),
35 | ]
36 |
--------------------------------------------------------------------------------
/backend/problem/migrations/0009_auto_20171011_1214.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.4 on 2017-10-11 12:14
3 | from __future__ import unicode_literals
4 |
5 | import django.contrib.postgres.fields.jsonb
6 | from django.db import migrations
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | dependencies = [
12 | ('problem', '0008_auto_20170923_1318'),
13 | ]
14 |
15 | operations = [
16 | migrations.AlterField(
17 | model_name='problem',
18 | name='languages',
19 | field=django.contrib.postgres.fields.jsonb.JSONField(),
20 | ),
21 | migrations.AlterField(
22 | model_name='problem',
23 | name='samples',
24 | field=django.contrib.postgres.fields.jsonb.JSONField(),
25 | ),
26 | migrations.AlterField(
27 | model_name='problem',
28 | name='statistic_info',
29 | field=django.contrib.postgres.fields.jsonb.JSONField(default=dict),
30 | ),
31 | migrations.AlterField(
32 | model_name='problem',
33 | name='template',
34 | field=django.contrib.postgres.fields.jsonb.JSONField(),
35 | ),
36 | migrations.AlterField(
37 | model_name='problem',
38 | name='test_case_score',
39 | field=django.contrib.postgres.fields.jsonb.JSONField(),
40 | ),
41 | migrations.AlterModelOptions(
42 | name='problem',
43 | options={'ordering': ('create_time',)},
44 | ),
45 | ]
46 |
--------------------------------------------------------------------------------
/backend/problem/migrations/0010_problem_spj_compile_ok.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.4 on 2017-11-16 12:42
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('problem', '0009_auto_20171011_1214'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='problem',
17 | name='spj_compile_ok',
18 | field=models.BooleanField(default=False),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/backend/problem/migrations/0011_fix_problem_ac_count.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | from django.db import migrations
5 | from django.db.models import Count
6 |
7 |
8 | def fix_problem_count_bugs(apps, schema_editor):
9 | Submission = apps.get_model("submission", "Submission")
10 | Problem = apps.get_model("problem", "Problem")
11 |
12 | for item in Problem.objects.filter(contest__isnull=True):
13 | submissions = Submission.objects.filter(problem=item)
14 | item.submission_number = submissions.count()
15 | results_count = submissions.values('result').annotate(count=Count('result')).order_by('result')
16 | info = dict()
17 | item.accepted_number = 0
18 | for stat in results_count:
19 | result = stat["result"]
20 | if result == 0:
21 | item.accepted_number = stat["count"]
22 | info[str(result)] = stat["count"]
23 | item.statistic_info = info
24 | item.save(update_fields=["submission_number", "accepted_number", "statistic_info"])
25 |
26 |
27 | class Migration(migrations.Migration):
28 | dependencies = [
29 | ('problem', '0010_problem_spj_compile_ok'),
30 | ('submission', '0009_delete_user_output'),
31 | ]
32 |
33 | operations = [
34 | migrations.RunPython(fix_problem_count_bugs, reverse_code=migrations.RunPython.noop)
35 | ]
36 |
--------------------------------------------------------------------------------
/backend/problem/migrations/0013_problem_io_mode.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.7 on 2019-03-12 07:13
2 |
3 | import django.contrib.postgres.fields.jsonb
4 | from django.db import migrations
5 | import problem.models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('problem', '0012_auto_20180501_0436'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='problem',
17 | name='io_mode',
18 | field=django.contrib.postgres.fields.jsonb.JSONField(default=problem.models._default_io_mode),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/backend/problem/migrations/0014_problem_share_submission.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.1.7 on 2019-03-13 09:38
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('problem', '0013_problem_io_mode'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='problem',
15 | name='share_submission',
16 | field=models.BooleanField(default=False),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/problem/migrations/0015_auto_20210730_2244.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.24 on 2021-07-30 13:44
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('assignment', '0001_initial'),
11 | ('problem', '0014_problem_share_submission'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='problem',
17 | name='assignment',
18 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='assignment.Assignment'),
19 | ),
20 | migrations.AddField(
21 | model_name='problem',
22 | name='type',
23 | field=models.TextField(default='Problem'),
24 | ),
25 | ]
26 |
--------------------------------------------------------------------------------
/backend/problem/migrations/0016_auto_20210801_2045.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.24 on 2021-08-01 11:45
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('problem', '0015_auto_20210730_2244'),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name='problem',
16 | name='assignment',
17 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='problems', to='assignment.Assignment'),
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/backend/problem/migrations/0017_auto_20210814_1415.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.24 on 2021-08-14 05:15
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('problem', '0016_auto_20210801_2045'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='problem',
15 | name='type',
16 | field=models.TextField(default='Problem', null=True),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/problem/migrations/0019_problem_bank.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.12 on 2022-03-10 08:20
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('problem', '0018_auto_20211226_1458'),
10 | ]
11 |
12 | operations = [
13 | migrations.AddField(
14 | model_name='problem',
15 | name='bank',
16 | field=models.BooleanField(default=False),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/problem/migrations/0020_auto_20220327_2013.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.12 on 2022-03-27 11:13
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('problem', '0019_problem_bank'),
11 | ]
12 |
13 | operations = [
14 | migrations.CreateModel(
15 | name='ProblemSetGroup',
16 | fields=[
17 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
18 | ('title', models.TextField()),
19 | ('button_type', models.TextField()),
20 | ('is_disabled', models.BooleanField()),
21 | ],
22 | ),
23 | migrations.CreateModel(
24 | name='ProblemSet',
25 | fields=[
26 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
27 | ('title', models.TextField()),
28 | ('color', models.TextField()),
29 | ('is_disabled', models.BooleanField()),
30 | ('is_public', models.BooleanField()),
31 | ('problem_set_group', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='problem_set', to='problem.problemsetgroup')),
32 | ],
33 | ),
34 | migrations.AddField(
35 | model_name='problem',
36 | name='problem_set',
37 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='problem.problemset'),
38 | ),
39 | ]
40 |
--------------------------------------------------------------------------------
/backend/problem/migrations/0021_auto_20220329_1451.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.12 on 2022-03-29 05:51
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('problem', '0020_auto_20220327_2013'),
10 | ]
11 |
12 | operations = [
13 | migrations.RemoveField(
14 | model_name='problem',
15 | name='problem_set',
16 | ),
17 | migrations.AddField(
18 | model_name='problem',
19 | name='problem_set',
20 | field=models.ManyToManyField(null=True, to='problem.ProblemSet'),
21 | ),
22 | ]
23 |
--------------------------------------------------------------------------------
/backend/problem/migrations/0022_alter_problem_problem_set.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.12 on 2022-03-29 05:52
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('problem', '0021_auto_20220329_1451'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='problem',
15 | name='problem_set',
16 | field=models.ManyToManyField(blank=True, to='problem.ProblemSet'),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/problem/migrations/0023_auto_20220329_1517.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.12 on 2022-03-29 06:17
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('problem', '0022_alter_problem_problem_set'),
10 | ]
11 |
12 | operations = [
13 | migrations.RemoveField(
14 | model_name='problem',
15 | name='problem_set',
16 | ),
17 | migrations.AddField(
18 | model_name='problemset',
19 | name='problems',
20 | field=models.ManyToManyField(blank=True, to='problem.Problem'),
21 | ),
22 | ]
23 |
--------------------------------------------------------------------------------
/backend/problem/migrations/0024_alter_problemset_problems.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.12 on 2022-03-29 16:43
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('problem', '0023_auto_20220329_1517'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='problemset',
15 | name='problems',
16 | field=models.ManyToManyField(blank=True, related_name='problem_set', to='problem.Problem'),
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/problem/migrations/0025_alter_problemset_problem_set_group.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.12 on 2022-03-30 06:33
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('problem', '0024_alter_problemset_problems'),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name='problemset',
16 | name='problem_set_group',
17 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='problem_set', to='problem.problemsetgroup'),
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/backend/problem/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/problem/migrations/__init__.py
--------------------------------------------------------------------------------
/backend/problem/urls/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/problem/urls/__init__.py
--------------------------------------------------------------------------------
/backend/problem/urls/admin.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from ..views.admin import (ContestProblemAPI, ProblemAPI, ProblemLevelAPIView, TestCaseAPI, MakeContestProblemPublicAPIView,
4 | CompileSPJAPI, AddContestProblemAPI, TestCaseTextAPI, ProblemSetGroupAPI, ProblemSetAPI)
5 |
6 | urlpatterns = [
7 | path("test_case/", TestCaseAPI.as_view(), name="test_case_api"),
8 | path("compile_spj/", CompileSPJAPI.as_view(), name="compile_spj"),
9 | path("problem/", ProblemAPI.as_view(), name="problem_admin_api"),
10 | path("contest/problem/", ContestProblemAPI.as_view(), name="contest_problem_admin_api"),
11 | path("contest_problem/make_public/", MakeContestProblemPublicAPIView.as_view(), name="make_public_api"),
12 | path("contest/add_problem_from_public/", AddContestProblemAPI.as_view(), name="add_contest_problem_from_public_api"),
13 | path("testcase_text/", TestCaseTextAPI.as_view(), name="testcase_text_api"),
14 | path("problem_level_count/", ProblemLevelAPIView.as_view(), name="problem_level_count"),
15 | path("problemset/group/", ProblemSetGroupAPI.as_view(), name="problem_set_group_admin_api"),
16 | path("problemset/", ProblemSetAPI.as_view(), name="problem_set_admin_api"),
17 | ]
18 |
--------------------------------------------------------------------------------
/backend/problem/urls/oj.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from ..views.oj import ProblemTagAPI, ProblemAPI, ContestProblemAPI, BankProblemAPI, ProblemSetGroupAPI, ProblemSetAPI
4 |
5 | urlpatterns = [
6 | path("problem/tags/", ProblemTagAPI.as_view(), name="problem_tag_list_api"),
7 | path("problem/", ProblemAPI.as_view(), name="problem_api"),
8 | path("contest/problem/", ContestProblemAPI.as_view(), name="contest_problem_api"),
9 | path("bank/problem/", BankProblemAPI.as_view(), name="bank_problem_api"),
10 | path("problemset/group/", ProblemSetGroupAPI.as_view(), name="problem_set_group_api"),
11 | path("problemset/", ProblemSetAPI.as_view(), name="problem_set_api"),
12 | ]
13 |
--------------------------------------------------------------------------------
/backend/problem/urls/professor.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from ..views.professor import AssignmentProblemAPI, AddAssignmentProblemAPI
4 |
5 | urlpatterns = [
6 | path("course/assignment/problem/", AssignmentProblemAPI.as_view(), name="assignment_problem_professor_api"),
7 | path("course/assignment/add_problem_from_public/", AddAssignmentProblemAPI.as_view(), name="add_assignment_problem_from_public_api"),
8 | ]
9 |
--------------------------------------------------------------------------------
/backend/problem/urls/student.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from ..views.student import AssignmentProblemAPI
4 |
5 | urlpatterns = [
6 | path("course/assignment/problem/", AssignmentProblemAPI.as_view(), name="assignment_problem_student_api"),
7 | ]
8 |
--------------------------------------------------------------------------------
/backend/problem/utils.py:
--------------------------------------------------------------------------------
1 | import re
2 | from functools import lru_cache
3 |
4 |
5 | TEMPLATE_BASE = """//PREPEND BEGIN
6 | {}
7 | //PREPEND END
8 |
9 | //TEMPLATE BEGIN
10 | {}
11 | //TEMPLATE END
12 |
13 | //APPEND BEGIN
14 | {}
15 | //APPEND END"""
16 |
17 |
18 | @lru_cache(maxsize=100)
19 | def parse_problem_template(template_str):
20 | prepend = re.findall(r"//PREPEND BEGIN\n([\s\S]+?)//PREPEND END", template_str)
21 | template = re.findall(r"//TEMPLATE BEGIN\n([\s\S]+?)//TEMPLATE END", template_str)
22 | append = re.findall(r"//APPEND BEGIN\n([\s\S]+?)//APPEND END", template_str)
23 | return {"prepend": prepend[0] if prepend else "",
24 | "template": template[0] if template else "",
25 | "append": append[0] if append else ""}
26 |
27 |
28 | @lru_cache(maxsize=100)
29 | def build_problem_template(prepend, template, append):
30 | return TEMPLATE_BASE.format(prepend, template, append)
31 |
--------------------------------------------------------------------------------
/backend/problem/views/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/problem/views/__init__.py
--------------------------------------------------------------------------------
/backend/qna/migrations/0002_question_content_question_course_id_and_more.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.1 on 2022-01-09 08:54
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('course', '0004_registration_bookmark'),
11 | ('qna', '0001_initial'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='question',
17 | name='content',
18 | field=models.TextField(null=True),
19 | ),
20 | migrations.AddField(
21 | model_name='question',
22 | name='course_id',
23 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='course.course'),
24 | ),
25 | migrations.AddField(
26 | model_name='question',
27 | name='last_update_time',
28 | field=models.DateTimeField(null=True),
29 | ),
30 | migrations.AddField(
31 | model_name='question',
32 | name='status',
33 | field=models.BooleanField(null=True),
34 | ),
35 | migrations.AddField(
36 | model_name='question',
37 | name='title',
38 | field=models.TextField(null=True),
39 | ),
40 | ]
41 |
--------------------------------------------------------------------------------
/backend/qna/migrations/0003_alter_question_content_alter_question_course_id_and_more.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.1 on 2022-01-09 08:56
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('course', '0004_registration_bookmark'),
11 | ('qna', '0002_question_content_question_course_id_and_more'),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterField(
16 | model_name='question',
17 | name='content',
18 | field=models.TextField(),
19 | ),
20 | migrations.AlterField(
21 | model_name='question',
22 | name='course_id',
23 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='course.course'),
24 | ),
25 | migrations.AlterField(
26 | model_name='question',
27 | name='last_update_time',
28 | field=models.DateTimeField(),
29 | ),
30 | migrations.AlterField(
31 | model_name='question',
32 | name='status',
33 | field=models.BooleanField(),
34 | ),
35 | migrations.AlterField(
36 | model_name='question',
37 | name='title',
38 | field=models.TextField(),
39 | ),
40 | ]
41 |
--------------------------------------------------------------------------------
/backend/qna/migrations/0004_rename_course_id_question_course_and_more.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 4.0.1 on 2022-01-10 11:55
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('qna', '0003_alter_question_content_alter_question_course_id_and_more'),
10 | ]
11 |
12 | operations = [
13 | migrations.RenameField(
14 | model_name='question',
15 | old_name='course_id',
16 | new_name='course',
17 | ),
18 | migrations.AlterField(
19 | model_name='question',
20 | name='last_update_time',
21 | field=models.DateTimeField(auto_now=True),
22 | ),
23 | migrations.AlterField(
24 | model_name='question',
25 | name='status',
26 | field=models.BooleanField(default=True),
27 | ),
28 | ]
29 |
--------------------------------------------------------------------------------
/backend/qna/migrations/0005_rename_status_question_is_open.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.4 on 2022-02-20 23:43
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('qna', '0004_rename_course_id_question_course_and_more'),
10 | ]
11 |
12 | operations = [
13 | migrations.RenameField(
14 | model_name='question',
15 | old_name='status',
16 | new_name='is_open',
17 | ),
18 | ]
19 |
--------------------------------------------------------------------------------
/backend/qna/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/qna/migrations/__init__.py
--------------------------------------------------------------------------------
/backend/qna/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | from account.models import User
4 | from course.models import Course
5 |
6 |
7 | class AdminType(object):
8 | PROFESSOR = "Professor"
9 | CREATOR = "Creator"
10 | NONE = "None"
11 |
12 |
13 | class Question(models.Model):
14 | created_by = models.ForeignKey(User, on_delete=models.CASCADE)
15 | course = models.ForeignKey(Course, on_delete=models.CASCADE)
16 | title = models.TextField()
17 | content = models.TextField()
18 | create_time = models.DateTimeField(auto_now_add=True)
19 | last_update_time = models.DateTimeField(auto_now=True)
20 | is_open = models.BooleanField(default=True)
21 |
22 | class Meta:
23 | db_table = "question"
24 | ordering = ("-create_time",)
25 |
26 |
27 | class Answer(models.Model):
28 | question = models.ForeignKey(Question, on_delete=models.CASCADE)
29 | created_by = models.ForeignKey(User, on_delete=models.CASCADE)
30 | content = models.TextField()
31 | create_time = models.DateTimeField(auto_now_add=True)
32 | last_update_time = models.DateTimeField(auto_now=True)
33 | admin_type = models.TextField(default=AdminType.NONE)
34 |
35 | class Meta:
36 | db_table = "answer"
37 | ordering = ("-create_time",)
38 |
39 | def update_admin_type(self, user):
40 | if user.is_authenticated and user.is_admin_role():
41 | self.admin_type = AdminType.PROFESSOR
42 | elif user.is_question_admin(self.question):
43 | self.admin_type = AdminType.CREATOR
44 | self.save(update_fields=["admin_type"])
45 |
46 | def name(self):
47 | if self.admin_type == AdminType.PROFESSOR:
48 | return self.created_by.userprofile.real_name
49 | return self.created_by.username
50 |
--------------------------------------------------------------------------------
/backend/qna/serializers.py:
--------------------------------------------------------------------------------
1 | from utils.api import UsernameSerializer, serializers
2 | from .models import Question
3 | from course.serializers import CourseSerializer
4 |
5 |
6 | class QuestionSerializer(serializers.ModelSerializer):
7 | created_by = UsernameSerializer()
8 | course = CourseSerializer()
9 |
10 | class Meta:
11 | model = Question
12 | fields = "__all__"
13 |
14 |
15 | class CreateQuestionSerializer(serializers.Serializer):
16 | course_id = serializers.IntegerField()
17 | title = serializers.CharField(max_length=128)
18 | content = serializers.CharField(max_length=1024 * 1024 * 8)
19 |
20 |
21 | class EditQuestionSerializer(serializers.Serializer):
22 | id = serializers.IntegerField()
23 | title = serializers.CharField(max_length=128)
24 | content = serializers.CharField(max_length=1024 * 1024 * 8)
25 | is_open = serializers.BooleanField()
26 |
27 |
28 | class AnswerSerializer(serializers.Serializer):
29 | id = serializers.IntegerField()
30 | name = serializers.CharField()
31 | admin_type = serializers.CharField()
32 | content = serializers.CharField()
33 | last_update_time = serializers.DateTimeField()
34 |
35 |
36 | class CreateAnswerSerializer(serializers.Serializer):
37 | question_id = serializers.IntegerField()
38 | content = serializers.CharField()
39 | closure = serializers.BooleanField()
40 |
41 |
42 | class EditAnswerSerializer(serializers.Serializer):
43 | id = serializers.IntegerField()
44 | content = serializers.CharField()
45 |
--------------------------------------------------------------------------------
/backend/qna/urls/professor.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from ..views.professor import QuestionAPI
4 |
5 | urlpatterns = [
6 | path("course/question/", QuestionAPI.as_view(), name="question_professor_api"),
7 | ]
8 |
--------------------------------------------------------------------------------
/backend/qna/urls/student.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from ..views.student import QuestionAPI, AnswerAPI
4 |
5 | urlpatterns = [
6 | path("course/question/", QuestionAPI.as_view(), name="question_api"),
7 | path("course/question/answer/", AnswerAPI.as_view(), name="answer_api")
8 | ]
9 |
--------------------------------------------------------------------------------
/backend/qna/views/professor.py:
--------------------------------------------------------------------------------
1 | from utils.decorators import admin_role_required
2 | from drf_yasg.utils import swagger_auto_schema
3 | from drf_yasg import openapi
4 |
5 | from utils.api import APIView
6 | from ..serializers import QuestionSerializer
7 | from ..models import Question
8 |
9 |
10 | class QuestionAPI(APIView):
11 | @swagger_auto_schema(
12 | manual_parameters=[
13 | openapi.Parameter(
14 | name="course_id",
15 | in_=openapi.IN_QUERY,
16 | description="Id of course",
17 | required=True,
18 | type=openapi.TYPE_INTEGER,
19 | ),
20 | openapi.Parameter(
21 | name="question_id", in_=openapi.IN_QUERY,
22 | type=openapi.TYPE_INTEGER,
23 | required=False
24 | ),
25 | ],
26 | operation_description="Get Question"
27 | )
28 | @admin_role_required
29 | def get(self, request):
30 | course_id = request.GET.get("course_id")
31 | question_id = request.GET.get("question_id")
32 | if question_id:
33 | try:
34 | question = Question.objects.get(id=question_id, course_id=course_id)
35 | return self.success(QuestionSerializer(question).data)
36 | except Question.DoesNotExist:
37 | return self.error("Question does not exist")
38 | question = Question.objects.all().order_by("-create_time")
39 |
40 | return self.success(self.paginate_data(request, question, QuestionSerializer))
41 |
--------------------------------------------------------------------------------
/backend/run_test.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import getopt
3 | import os
4 | import sys
5 |
6 | opts, args = getopt.getopt(sys.argv[1:], "cm:", ["coverage=", "module="])
7 |
8 | is_coverage = False
9 | test_module = ""
10 | setting = "oj.settings"
11 |
12 | for opt, arg in opts:
13 | if opt in ["-c", "--coverage"]:
14 | is_coverage = True
15 | if opt in ["-m", "--module"]:
16 | test_module = arg
17 |
18 | print(f"Coverage: {is_coverage}")
19 | print(f"Module: {(test_module if test_module else 'All')}")
20 |
21 | print("running flake8...")
22 | if os.system("flake8 --statistics --config .flake8 ."):
23 | exit()
24 |
25 | ret = os.system(f'coverage run --include="$PWD/*" manage.py test {test_module} --settings={setting}')
26 |
27 | if not ret and is_coverage:
28 | os.system('echo "\n----------------------------------------------------------"')
29 | os.system('echo "🌎 Open http://localhost:9000 to check coverage result."')
30 | os.system('echo "✋ Press Ctrl + C to stop serving.\n"')
31 | os.system("coverage html && npx --yes http-server htmlcov -s -p 9000")
32 |
--------------------------------------------------------------------------------
/backend/submission/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/submission/__init__.py
--------------------------------------------------------------------------------
/backend/submission/migrations/0001_initial.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2017-05-09 06:41
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 | import jsonfield.fields
7 | import utils.models
8 | import utils.shortcuts
9 |
10 |
11 | class Migration(migrations.Migration):
12 |
13 | initial = True
14 |
15 | dependencies = [
16 | ]
17 |
18 | operations = [
19 | migrations.CreateModel(
20 | name='Submission',
21 | fields=[
22 | ('id', models.CharField(db_index=True, default=utils.shortcuts.rand_str, max_length=32, primary_key=True, serialize=False)),
23 | ('contest_id', models.IntegerField(db_index=True, null=True)),
24 | ('problem_id', models.IntegerField(db_index=True)),
25 | ('created_time', models.DateTimeField(auto_now_add=True)),
26 | ('user_id', models.IntegerField(db_index=True)),
27 | ('code', utils.models.RichTextField()),
28 | ('result', models.IntegerField(default=6)),
29 | ('info', jsonfield.fields.JSONField(default={})),
30 | ('language', models.CharField(max_length=20)),
31 | ('shared', models.BooleanField(default=False)),
32 | ('accepted_time', models.IntegerField(blank=True, null=True)),
33 | ('accepted_info', jsonfield.fields.JSONField(default={})),
34 | ],
35 | options={
36 | 'db_table': 'submission',
37 | },
38 | ),
39 | ]
40 |
--------------------------------------------------------------------------------
/backend/submission/migrations/0002_auto_20170509_1203.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.9.6 on 2017-05-09 12:03
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('submission', '0001_initial'),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterField(
16 | model_name='submission',
17 | name='code',
18 | field=models.TextField(),
19 | ),
20 | migrations.RenameField(
21 | model_name='submission',
22 | old_name='accepted_info',
23 | new_name='statistic_info',
24 | ),
25 | migrations.RemoveField(
26 | model_name='submission',
27 | name='accepted_time',
28 | ),
29 | migrations.RenameField(
30 | model_name='submission',
31 | old_name='created_time',
32 | new_name='create_time',
33 | ),
34 | migrations.AlterModelOptions(
35 | name='submission',
36 | options={'ordering': ('-create_time',)},
37 | )
38 | ]
39 |
--------------------------------------------------------------------------------
/backend/submission/migrations/0005_submission_username.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.4 on 2017-08-26 03:47
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('submission', '0002_auto_20170509_1203'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='submission',
17 | name='username',
18 | field=models.CharField(default="", max_length=30),
19 | preserve_default=False,
20 | ),
21 | ]
22 |
--------------------------------------------------------------------------------
/backend/submission/migrations/0006_auto_20170830_1154.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.4 on 2017-08-30 11:54
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('submission', '0005_submission_username'),
12 | ]
13 |
14 | operations = [
15 | migrations.AlterField(
16 | model_name='submission',
17 | name='result',
18 | field=models.IntegerField(db_index=True, default=6),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/backend/submission/migrations/0007_auto_20170923_1318.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.4 on 2017-09-23 13:18
3 | from __future__ import unicode_literals
4 |
5 | import django.contrib.postgres.fields.jsonb
6 | from django.db import migrations, models
7 | import django.db.models.deletion
8 |
9 |
10 | class Migration(migrations.Migration):
11 |
12 | dependencies = [
13 | ('submission', '0006_auto_20170830_1154'),
14 | ]
15 |
16 | operations = [
17 | migrations.AlterField(
18 | model_name='submission',
19 | name='contest_id',
20 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='contest.Contest'),
21 | ),
22 | migrations.AlterField(
23 | model_name='submission',
24 | name='problem_id',
25 | field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='problem.Problem'),
26 | ),
27 | migrations.RenameField(
28 | model_name='submission',
29 | old_name='contest_id',
30 | new_name='contest',
31 | ),
32 | migrations.RenameField(
33 | model_name='submission',
34 | old_name='problem_id',
35 | new_name='problem',
36 | ),
37 | migrations.AlterField(
38 | model_name='submission',
39 | name='info',
40 | field=django.contrib.postgres.fields.jsonb.JSONField(default=dict),
41 | ),
42 | migrations.AlterField(
43 | model_name='submission',
44 | name='statistic_info',
45 | field=django.contrib.postgres.fields.jsonb.JSONField(default=dict),
46 | ),
47 | ]
48 |
--------------------------------------------------------------------------------
/backend/submission/migrations/0008_submission_ip.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.4 on 2017-11-10 06:57
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 |
7 |
8 | class Migration(migrations.Migration):
9 |
10 | dependencies = [
11 | ('submission', '0007_auto_20170923_1318'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='submission',
17 | name='ip',
18 | field=models.CharField(blank=True, max_length=32, null=True),
19 | ),
20 | ]
21 |
--------------------------------------------------------------------------------
/backend/submission/migrations/0009_delete_user_output.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | from django.db import migrations
5 |
6 |
7 | def delete_user_output(apps, schema_editor):
8 | Submission = apps.get_model("submission", "Submission")
9 | for item in Submission.objects.all():
10 | if "data" in item.info and isinstance(item.info["data"], list):
11 | for index in range(len(item.info["data"])):
12 | item.info["data"][index]["output"] = ""
13 | item.save()
14 |
15 |
16 | class Migration(migrations.Migration):
17 |
18 | dependencies = [
19 | ('submission', '0008_submission_ip'),
20 | ]
21 |
22 | operations = [
23 | migrations.RunPython(delete_user_output, reverse_code=migrations.RunPython.noop)
24 | ]
25 |
--------------------------------------------------------------------------------
/backend/submission/migrations/0011_fix_submission_number.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | from __future__ import unicode_literals
3 |
4 | from django.db import migrations
5 |
6 |
7 | def fix_rejudge_bugs(apps, schema_editor):
8 | Submission = apps.get_model("submission", "Submission")
9 | User = apps.get_model("account", "User")
10 |
11 | for user in User.objects.all():
12 | submissions = Submission.objects.filter(user_id=user.id, contest__isnull=True)
13 | profile = user.userprofile
14 | profile.submission_number = submissions.count()
15 | profile.accepted_number = submissions.filter(result=0).count()
16 | profile.save(update_fields=["submission_number", "accepted_number"])
17 |
18 |
19 | class Migration(migrations.Migration):
20 | dependencies = [
21 | ('submission', '0009_delete_user_output'),
22 | ('problem', '0010_problem_spj_compile_ok'),
23 | ]
24 |
25 | operations = [
26 | migrations.RunPython(fix_rejudge_bugs, reverse_code=migrations.RunPython.noop)
27 | ]
28 |
--------------------------------------------------------------------------------
/backend/submission/migrations/0012_auto_20180501_0436.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # Generated by Django 1.11.3 on 2018-05-01 04:36
3 | from __future__ import unicode_literals
4 |
5 | from django.db import migrations, models
6 | import utils.shortcuts
7 |
8 |
9 | class Migration(migrations.Migration):
10 |
11 | dependencies = [
12 | ('submission', '0011_fix_submission_number'),
13 | ]
14 |
15 | operations = [
16 | migrations.AlterField(
17 | model_name='submission',
18 | name='id',
19 | field=models.TextField(db_index=True, default=utils.shortcuts.rand_str, primary_key=True, serialize=False),
20 | ),
21 | migrations.AlterField(
22 | model_name='submission',
23 | name='ip',
24 | field=models.TextField(null=True),
25 | ),
26 | migrations.AlterField(
27 | model_name='submission',
28 | name='language',
29 | field=models.TextField(),
30 | ),
31 | migrations.AlterField(
32 | model_name='submission',
33 | name='username',
34 | field=models.TextField(),
35 | ),
36 | ]
37 |
--------------------------------------------------------------------------------
/backend/submission/migrations/0013_auto_20210730_2244.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.24 on 2021-07-30 13:44
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('assignment', '0001_initial'),
11 | ('submission', '0012_auto_20180501_0436'),
12 | ]
13 |
14 | operations = [
15 | migrations.AddField(
16 | model_name='submission',
17 | name='assignment',
18 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='assignment.Assignment'),
19 | ),
20 | migrations.AddField(
21 | model_name='submission',
22 | name='score',
23 | field=models.IntegerField(null=True),
24 | ),
25 | ]
26 |
--------------------------------------------------------------------------------
/backend/submission/migrations/0014_auto_20210802_2115.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.24 on 2021-08-02 12:15
2 |
3 | from django.db import migrations, models
4 | import django.db.models.deletion
5 |
6 |
7 | class Migration(migrations.Migration):
8 |
9 | dependencies = [
10 | ('submission', '0013_auto_20210730_2244'),
11 | ]
12 |
13 | operations = [
14 | migrations.AlterField(
15 | model_name='submission',
16 | name='assignment',
17 | field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='submissions', to='assignment.Assignment'),
18 | ),
19 | ]
20 |
--------------------------------------------------------------------------------
/backend/submission/migrations/0015_remove_submission_score.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 2.2.24 on 2021-08-08 08:09
2 |
3 | from django.db import migrations
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('submission', '0014_auto_20210802_2115'),
10 | ]
11 |
12 | operations = [
13 | migrations.RemoveField(
14 | model_name='submission',
15 | name='score',
16 | ),
17 | ]
18 |
--------------------------------------------------------------------------------
/backend/submission/migrations/0016_auto_20211226_1458.py:
--------------------------------------------------------------------------------
1 | # Generated by Django 3.2.5 on 2021-12-26 05:58
2 |
3 | from django.db import migrations, models
4 |
5 |
6 | class Migration(migrations.Migration):
7 |
8 | dependencies = [
9 | ('submission', '0015_remove_submission_score'),
10 | ]
11 |
12 | operations = [
13 | migrations.AlterField(
14 | model_name='submission',
15 | name='info',
16 | field=models.JSONField(default=dict),
17 | ),
18 | migrations.AlterField(
19 | model_name='submission',
20 | name='statistic_info',
21 | field=models.JSONField(default=dict),
22 | ),
23 | ]
24 |
--------------------------------------------------------------------------------
/backend/submission/migrations/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/submission/migrations/__init__.py
--------------------------------------------------------------------------------
/backend/submission/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from .views import (SubmissionAPI, SubmissionListAPI, ContestSubmissionListAPI, AssignmentSubmissionListAPI,
4 | AssignmentSubmissionListProfessorAPI, SubmissionExistsAPI, EditSubmissionScoreAPI)
5 |
6 | urlpatterns = [
7 | path("submission/", SubmissionAPI.as_view(), name="submission_api"),
8 | path("submissions/", SubmissionListAPI.as_view(), name="submission_list_api"),
9 | path("submission_exists/", SubmissionExistsAPI.as_view(), name="submission_exists"),
10 | path("contest_submissions/", ContestSubmissionListAPI.as_view(), name="contest_submission_list_api"),
11 | path("assignment_submissions/", AssignmentSubmissionListAPI.as_view(), name="assignment_submission_list_api"),
12 | path("assignment_submissions_professor/", AssignmentSubmissionListProfessorAPI.as_view(), name="assignment_submission_list_professor_api"),
13 | path("edit_submission_score/", EditSubmissionScoreAPI.as_view(), name="edit_submission_score_api"),
14 | ]
15 |
--------------------------------------------------------------------------------
/backend/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/utils/__init__.py
--------------------------------------------------------------------------------
/backend/utils/api/__init__.py:
--------------------------------------------------------------------------------
1 | from ._serializers import * # NOQA
2 | from .api import * # NOQA
3 |
--------------------------------------------------------------------------------
/backend/utils/api/_serializers.py:
--------------------------------------------------------------------------------
1 | from rest_framework import serializers
2 |
3 |
4 | class UsernameSerializer(serializers.Serializer):
5 | id = serializers.IntegerField()
6 | username = serializers.CharField()
7 | real_name = serializers.SerializerMethodField()
8 |
9 | def __init__(self, *args, **kwargs):
10 | self.need_real_name = kwargs.pop("need_real_name", False)
11 | super().__init__(*args, **kwargs)
12 |
13 | def get_real_name(self, obj) -> str:
14 | return obj.userprofile.real_name if self.need_real_name else None
15 |
--------------------------------------------------------------------------------
/backend/utils/cache.py:
--------------------------------------------------------------------------------
1 | from django.core.cache import cache, caches # noqa
2 | from django.conf import settings # noqa
3 |
4 | from django_redis.cache import RedisCache
5 | from django_redis.client.default import DefaultClient
6 |
7 |
8 | class MyRedisClient(DefaultClient):
9 | def __getattr__(self, item):
10 | client = self.get_client(write=True)
11 | return getattr(client, item)
12 |
13 | def redis_incr(self, key, count=1):
14 | """
15 | Django's default incr will throw an exception when the key does not exist
16 | """
17 | client = self.get_client(write=True)
18 | return client.incr(key, count)
19 |
20 |
21 | class MyRedisCache(RedisCache):
22 | def __init__(self, server, params):
23 | super().__init__(server, params)
24 | self._client_cls = MyRedisClient
25 |
26 | def __getattr__(self, item):
27 | return getattr(self.client, item)
28 |
--------------------------------------------------------------------------------
/backend/utils/captcha/Menlo.ttc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/utils/captcha/Menlo.ttc
--------------------------------------------------------------------------------
/backend/utils/captcha/timesbi.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/utils/captcha/timesbi.ttf
--------------------------------------------------------------------------------
/backend/utils/captcha/views.py:
--------------------------------------------------------------------------------
1 | from . import Captcha
2 | from ..api import APIView
3 | from ..shortcuts import img2base64
4 |
5 |
6 | class CaptchaAPIView(APIView):
7 | def get(self, request):
8 | return self.success(img2base64(Captcha(request).get()))
9 |
--------------------------------------------------------------------------------
/backend/utils/constants.py:
--------------------------------------------------------------------------------
1 | class Choices:
2 | @classmethod
3 | def choices(cls):
4 | d = cls.__dict__
5 | return [d[item] for item in d.keys() if not item.startswith("__")]
6 |
7 |
8 | class ContestType:
9 | PUBLIC_CONTEST = "Public"
10 | PASSWORD_PROTECTED_CONTEST = "Password Protected"
11 |
12 |
13 | class ContestStatus:
14 | CONTEST_NOT_START = "1"
15 | CONTEST_ENDED = "-1"
16 | CONTEST_UNDERWAY = "0"
17 |
18 |
19 | class ContestRuleType(Choices):
20 | ACM = "ACM"
21 | OI = "OI"
22 |
23 |
24 | class AssignmentStatus:
25 | ASSIGNMENT_NOT_START = "1"
26 | ASSIGNMENT_ENDED = "-1"
27 | ASSIGNMENT_UNDERWAY = "0"
28 |
29 |
30 | class CacheKey:
31 | waiting_queue = "waiting_queue"
32 | contest_rank_cache = "contest_rank_cache"
33 | website_config = "website_config"
34 | auth_token_cache = "email_auth/token"
35 | auth_email_cache = "email_auth/email"
36 |
37 |
38 | class Difficulty(Choices):
39 | Level1 = "Level1"
40 | Level2 = "Level2"
41 | Level3 = "Level3"
42 | Level4 = "Level4"
43 | Level5 = "Level5"
44 | Level6 = "Level6"
45 | Level7 = "Level7"
46 |
47 |
48 | CONTEST_PASSWORD_SESSION_KEY = "contest_password"
49 |
--------------------------------------------------------------------------------
/backend/utils/management/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/utils/management/__init__.py
--------------------------------------------------------------------------------
/backend/utils/management/commands/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/backend/utils/management/commands/__init__.py
--------------------------------------------------------------------------------
/backend/utils/models.py:
--------------------------------------------------------------------------------
1 | from django.db import models
2 |
3 | from utils.xss_filter import XSSHtml
4 |
5 |
6 | class RichTextField(models.TextField):
7 | def get_prep_value(self, value):
8 | with XSSHtml() as parser:
9 | return parser.clean(value or "")
10 |
--------------------------------------------------------------------------------
/backend/utils/serializers.py:
--------------------------------------------------------------------------------
1 | from rest_framework import serializers
2 |
3 | from options.options import SysOptions
4 |
5 |
6 | class InvalidLanguage(serializers.ValidationError):
7 | def __init__(self, name):
8 | super().__init__(detail=f"{name} is not a valid language")
9 |
10 |
11 | class LanguageNameChoiceField(serializers.CharField):
12 | def to_internal_value(self, data):
13 | data = super().to_internal_value(data)
14 | if data and data not in SysOptions.language_names:
15 | raise InvalidLanguage(data)
16 | return data
17 |
18 |
19 | class SPJLanguageNameChoiceField(serializers.CharField):
20 | def to_internal_value(self, data):
21 | data = super().to_internal_value(data)
22 | if data and data not in SysOptions.spj_language_names:
23 | raise InvalidLanguage(data)
24 | return data
25 |
26 |
27 | class LanguageNameMultiChoiceField(serializers.ListField):
28 | def to_internal_value(self, data):
29 | data = super().to_internal_value(data)
30 | for item in data:
31 | if item not in SysOptions.language_names:
32 | raise InvalidLanguage(item)
33 | return data
34 |
35 |
36 | class SPJLanguageNameMultiChoiceField(serializers.ListField):
37 | def to_internal_value(self, data):
38 | data = super().to_internal_value(data)
39 | for item in data:
40 | if item not in SysOptions.spj_language_names:
41 | raise InvalidLanguage(item)
42 | return data
43 |
--------------------------------------------------------------------------------
/backend/utils/tasks.py:
--------------------------------------------------------------------------------
1 | import os
2 | import dramatiq
3 |
4 | from utils.shortcuts import DRAMATIQ_WORKER_ARGS
5 |
6 |
7 | @dramatiq.actor(**DRAMATIQ_WORKER_ARGS())
8 | def delete_files(*args):
9 | for item in args:
10 | try:
11 | os.remove(item)
12 | except Exception:
13 | pass
14 |
--------------------------------------------------------------------------------
/backend/utils/urls.py:
--------------------------------------------------------------------------------
1 | from django.urls import path
2 |
3 | from .views import SimditorImageUploadAPIView, SimditorFileUploadAPIView
4 |
5 | urlpatterns = [
6 | path("upload_image/", SimditorImageUploadAPIView.as_view(), name="upload_image"),
7 | path("upload_file/", SimditorFileUploadAPIView.as_view(), name="upload_file")
8 | ]
9 |
--------------------------------------------------------------------------------
/db_backup.sh:
--------------------------------------------------------------------------------
1 | docker exec -it oj-postgres pg_dumpall -c -U onlinejudge > db_backup_`date +%Y_%m_%d"_"%H_%M_%S`.sql
2 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3.8"
2 | services:
3 | oj-redis:
4 | image: redis:4.0-alpine
5 | container_name: oj-redis
6 | restart: always
7 | volumes:
8 | - ./data/redis:/data
9 |
10 | oj-postgres:
11 | image: postgres:10-alpine
12 | container_name: oj-postgres
13 | restart: always
14 | volumes:
15 | - ./data/postgres:/var/lib/postgresql/data
16 | environment:
17 | - POSTGRES_DB=onlinejudge
18 | - POSTGRES_USER=onlinejudge
19 | - POSTGRES_PASSWORD=onlinejudge
20 |
21 | judge-server:
22 | image: skkunpc/judge-server
23 | container_name: judge-server
24 | restart: always
25 | read_only: true
26 | cap_drop:
27 | - SETPCAP
28 | - MKNOD
29 | - NET_BIND_SERVICE
30 | - SYS_CHROOT
31 | - SETFCAP
32 | - FSETID
33 | tmpfs:
34 | - /tmp
35 | volumes:
36 | - ./data/backend/test_case:/test_case:ro
37 | - ./data/judge-server/log:/log
38 | - ./data/judge-server/run:/judger
39 | environment:
40 | - SERVICE_URL=http://judge-server:8080
41 | - BACKEND_URL=http://coding-platform:8000/api/judge_server_heartbeat/
42 | - TOKEN=CHANGE_THIS
43 | # - judger_debug=1
44 |
45 | coding-platform:
46 | image: ghcr.io/skkuding/coding-platform
47 | container_name: coding-platform
48 | restart: always
49 | depends_on:
50 | - oj-redis
51 | - oj-postgres
52 | - judge-server
53 | volumes:
54 | - ./data/backend:/data
55 | environment:
56 | - POSTGRES_DB=onlinejudge
57 | - POSTGRES_USER=onlinejudge
58 | - POSTGRES_PASSWORD=onlinejudge
59 | - JUDGE_SERVER_TOKEN=CHANGE_THIS
60 | ports:
61 | - "0.0.0.0:80:8000"
62 | - "0.0.0.0:443:1443"
63 |
--------------------------------------------------------------------------------
/frontend/.browserslistrc:
--------------------------------------------------------------------------------
1 | > 1%
2 | last 2 versions
3 | not dead
4 |
--------------------------------------------------------------------------------
/frontend/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.{js,jsx,ts,tsx,vue}]
2 | indent_style = space
3 | indent_size = 2
4 | end_of_line = lf
5 | insert_final_newline = true
6 | trim_trailing_whitespace = true
7 |
--------------------------------------------------------------------------------
/frontend/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | env: {
4 | node: true
5 | },
6 | extends: [
7 | 'plugin:vue/essential',
8 | '@vue/standard',
9 | 'plugin:cypress/recommended'
10 | ],
11 | parserOptions: {
12 | parser: '@babel/eslint-parser'
13 | },
14 | rules: {
15 | 'no-console': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
16 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off',
17 | 'vue/no-parsing-error': ['error', {
18 | 'x-invalid-end-tag': false
19 | }]
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/frontend/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/cli-plugin-babel/preset'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/frontend/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "baseUrl": "https://localhost"
3 | }
4 |
--------------------------------------------------------------------------------
/frontend/cypress/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io",
4 | "body": "Fixtures are a great way to mock data for responses to routes"
5 | }
6 |
--------------------------------------------------------------------------------
/frontend/cypress/integration/oj/problem/problem.spec.js:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | // describe -> context -> it
4 | context('When user tries to submit a problem (not in contest)', () => {
5 | beforeEach(() => {
6 | cy.loginSuperAdmin()
7 | cy.visit('problem')
8 | })
9 | // TODO: make it independent from admin_problem_spec (anti-pattern)
10 | it('Submit Code', () => {
11 | cy.fixture('problem-example').then((problemExample) => {
12 | cy.get('tbody > tr > .problem-title-field').contains(problemExample.problem.title).click()
13 | cy.get('[data-cy="toggle-language"] > .dropdown-toggle-split').click()
14 | cy.get('[data-cy="select-langauge"]').contains(problemExample.solution.language).click()
15 | cy.get('.CodeMirror-code').click().type(problemExample.solution.code, { delay: 0 })
16 | cy.get('[data-cy="button-submit"]').click()
17 | })
18 | })
19 | })
20 |
--------------------------------------------------------------------------------
/frontend/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | ///
2 | // ***********************************************************
3 | // This example plugins/index.js can be used to load plugins
4 | //
5 | // You can change the location of this file or turn off loading
6 | // the plugins file with the 'pluginsFile' configuration option.
7 | //
8 | // You can read more here:
9 | // https://on.cypress.io/plugins-guide
10 | // ***********************************************************
11 |
12 | // This function is called when a project is opened or re-opened (e.g. due to
13 | // the project's config changing)
14 |
15 | /**
16 | * @type {Cypress.PluginConfig}
17 | */
18 | // eslint-disable-next-line no-unused-vars
19 | module.exports = (on, config) => {
20 | // `on` is used to hook into various events Cypress emits
21 | // `config` is the resolved Cypress config
22 | }
23 |
--------------------------------------------------------------------------------
/frontend/cypress/support/commands.js:
--------------------------------------------------------------------------------
1 | Cypress.Commands.add('loginSuperAdmin', () => {
2 | cy.visit('')
3 | cy.getCookie('csrftoken')
4 | .should('exist')
5 | .then((csrftoken) => {
6 | cy.request({
7 | method: 'post',
8 | url: 'api/login/',
9 | body: {
10 | username: 'root',
11 | password: 'rootroot'
12 | },
13 | headers: {
14 | 'X-CSRFToken': csrftoken.value
15 | }
16 | })
17 | })
18 | const getStore = () => cy.window().its('app.$store')
19 | getStore().then(store => {
20 | store.dispatch('getProfile')
21 | })
22 | })
23 |
24 | // TODO: Register, Login by general user
25 |
--------------------------------------------------------------------------------
/frontend/cypress/support/index.js:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/index.js is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | import './commands'
17 |
18 | Cypress.Cookies.defaults({
19 | preserve: ['csrftoken', 'session_id']
20 | })
21 |
22 | Cypress.Commands.overwrite('request', (originalFn, ...args) => {
23 | let options = {}
24 | if (typeof args[0] === 'object' && args[0] !== null) {
25 | options = args[0]
26 | } else if (args.length === 1) {
27 | [options.url] = args
28 | } else if (args.length === 2) {
29 | [options.method, options.url] = args
30 | } else if (args.length === 3) {
31 | [options.method, options.url, options.body] = args
32 | }
33 | cy.getCookie('csrftoken')
34 | .should('exist')
35 | .then((csrftoken) => {
36 | const defaults = {
37 | headers: {
38 | 'X-CSRFToken': csrftoken.value
39 | }
40 | }
41 | return originalFn({ ...defaults, ...options, ...{ headers: { ...defaults.headers, ...options.headers } } })
42 | })
43 | })
44 |
--------------------------------------------------------------------------------
/frontend/postcss.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | plugins: {
3 | tailwindcss: {},
4 | autoprefixer: {}
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/frontend/public/cache.dll.json:
--------------------------------------------------------------------------------
1 | ["dll.c9dc8146.dll.js"]
--------------------------------------------------------------------------------
/frontend/src/assets/Cup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/frontend/src/assets/Cup.png
--------------------------------------------------------------------------------
/frontend/src/assets/icons/github.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/link.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/assets/icons/mail.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/frontend/src/assets/logos/codingPlatformLogo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/frontend/src/assets/logos/codingPlatformLogo.png
--------------------------------------------------------------------------------
/frontend/src/assets/logos/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/frontend/src/assets/logos/logo.png
--------------------------------------------------------------------------------
/frontend/src/assets/logos/signature.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/frontend/src/assets/logos/signature.png
--------------------------------------------------------------------------------
/frontend/src/assets/standing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/frontend/src/assets/standing.png
--------------------------------------------------------------------------------
/frontend/src/fonts/Manrope-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/frontend/src/fonts/Manrope-Bold.ttf
--------------------------------------------------------------------------------
/frontend/src/fonts/Manrope-ExtraBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/frontend/src/fonts/Manrope-ExtraBold.ttf
--------------------------------------------------------------------------------
/frontend/src/fonts/Manrope-ExtraLight.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/frontend/src/fonts/Manrope-ExtraLight.ttf
--------------------------------------------------------------------------------
/frontend/src/fonts/Manrope-Light.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/frontend/src/fonts/Manrope-Light.ttf
--------------------------------------------------------------------------------
/frontend/src/fonts/Manrope-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/frontend/src/fonts/Manrope-Medium.ttf
--------------------------------------------------------------------------------
/frontend/src/fonts/Manrope-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/frontend/src/fonts/Manrope-Regular.ttf
--------------------------------------------------------------------------------
/frontend/src/fonts/Manrope-SemiBold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/skkuding/skku-coding-platform/ffc2aec1f38122e9861593b69dd3d45d443d7e47/frontend/src/fonts/Manrope-SemiBold.ttf
--------------------------------------------------------------------------------
/frontend/src/pages/admin/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
13 |
14 |
31 |
--------------------------------------------------------------------------------
/frontend/src/pages/admin/components/KatexEditor.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Put LaTeX in the following format to use LaTeX in our editor.
5 | \( ... \), $$ ... $$, \[ ... \]
6 | For example, \( \pm\sqrt{a^2 + b^2} \) or $$ \pm\sqrt{a^2 + b^2} $$
7 |
8 |
9 | Input
10 |
11 |
16 |
17 |
18 | Output
19 |
20 |
21 |
22 |
23 |
24 |
34 |
35 |
40 |
--------------------------------------------------------------------------------
/frontend/src/pages/admin/components/btn/Cancel.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | Cancel
4 |
5 |
6 |
11 |
16 |
--------------------------------------------------------------------------------
/frontend/src/pages/admin/components/btn/IconBtn.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
13 |
14 |
29 |
--------------------------------------------------------------------------------
/frontend/src/pages/admin/components/btn/Save.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | Save
4 |
5 |
6 |
11 |
16 |
--------------------------------------------------------------------------------
/frontend/src/pages/admin/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | OnlineJudge
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/frontend/src/pages/admin/views/index.js:
--------------------------------------------------------------------------------
1 | import Dashboard from './general/Dashboard.vue'
2 | import Announcement from './general/Announcement.vue'
3 | import User from './general/User.vue'
4 | import Conf from './general/Conf.vue'
5 | import JudgeServer from './general/JudgeServer.vue'
6 | import PruneTestCase from './general/PruneTestCase.vue'
7 | import Problem from './problem/Problem.vue'
8 | import ProblemList from './problem/ProblemList.vue'
9 | import ProblemSet from './problem/ProblemSet.vue'
10 | import ContestList from './contest/ContestList.vue'
11 | import Contest from './contest/Contest.vue'
12 | import Login from './general/Login.vue'
13 | import Home from './Home.vue'
14 | import Banner from './general/Banner.vue'
15 |
16 | export {
17 | Announcement, User, Conf, JudgeServer, Problem, ProblemList, ProblemSet,
18 | Contest, ContestList, Login, Home, PruneTestCase, Dashboard, Banner
19 | }
20 |
--------------------------------------------------------------------------------
/frontend/src/pages/oj/components/Banner.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
14 |
15 |
16 |
17 |
18 |
43 |
44 |
49 |
--------------------------------------------------------------------------------
/frontend/src/pages/oj/components/ColorRoundButton.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
35 |
36 |
44 |
--------------------------------------------------------------------------------
/frontend/src/pages/oj/components/Highlight.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
38 |
39 |
49 |
--------------------------------------------------------------------------------
/frontend/src/pages/oj/components/PageTitle.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{text}}
4 |
5 |
6 |
7 |
21 |
--------------------------------------------------------------------------------
/frontend/src/pages/oj/components/PageTop.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
25 |
26 |
39 |
--------------------------------------------------------------------------------
/frontend/src/pages/oj/components/ShadowRoundButton.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
36 |
37 |
45 |
--------------------------------------------------------------------------------
/frontend/src/pages/oj/components/mixins/emitter.js:
--------------------------------------------------------------------------------
1 | function broadcast (componentName, eventName, params) {
2 | this.$children.forEach(child => {
3 | const name = child.$options.name
4 |
5 | if (name === componentName) {
6 | child.$emit.apply(child, [eventName].concat(params))
7 | } else {
8 | // todo 如果 params 是空数组,接收到的会是 undefined
9 | broadcast.apply(child, [componentName, eventName].concat([params]))
10 | }
11 | })
12 | }
13 |
14 | export default {
15 | methods: {
16 | dispatch (componentName, eventName, params) {
17 | let parent = this.$parent || this.$root
18 | let name = parent.$options.name
19 |
20 | while (parent && (!name || name !== componentName)) {
21 | parent = parent.$parent
22 |
23 | if (parent) {
24 | name = parent.$options.name
25 | }
26 | }
27 | if (parent) {
28 | parent.$emit.apply(parent, [eventName].concat(params))
29 | }
30 | },
31 | broadcast (componentName, eventName, params) {
32 | broadcast.call(this, componentName, eventName, params)
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/frontend/src/pages/oj/components/mixins/form.js:
--------------------------------------------------------------------------------
1 | import api from '@oj/api'
2 |
3 | export default {
4 | data () {
5 | return {
6 | captchaSrc: ''
7 | }
8 | },
9 | methods: {
10 | async validateForm (formName) {
11 | this.$refs[formName].validate((valid) => {
12 | if (!valid) {
13 | this.$error('please validate the error fields')
14 | } else {
15 | return valid
16 | }
17 | })
18 | },
19 | async getCaptchaSrc () {
20 | try {
21 | const res = await api.getCaptcha()
22 | this.captchaSrc = res.data.data
23 | } catch (err) {
24 | }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/frontend/src/pages/oj/components/mixins/index.js:
--------------------------------------------------------------------------------
1 | import Emitter from './emitter'
2 | import ProblemMixin from './problem'
3 | import FormMixin from './form'
4 |
5 | export { Emitter, ProblemMixin, FormMixin }
6 |
--------------------------------------------------------------------------------
/frontend/src/pages/oj/components/mixins/problem.js:
--------------------------------------------------------------------------------
1 | import utils from '@/utils/utils'
2 |
3 | export default {
4 | data () {
5 | return {
6 | statusColumn: false
7 | }
8 | },
9 | methods: {
10 | getACRate (ACCount, TotalCount) {
11 | return utils.getACRate(ACCount, TotalCount)
12 | },
13 | addStatusColumn (tableColumns, dataProblems) {
14 | // 已添加过直接返回
15 | if (this.statusColumn) return
16 | // 只在有做题记录时才添加column
17 | const needAdd = dataProblems.some((item, index) => {
18 | if (item.my_status !== null && item.my_status !== undefined) {
19 | return true
20 | }
21 | return false
22 | })
23 | if (!needAdd) {
24 | return
25 | }
26 | tableColumns.splice(0, 0, {
27 | width: 60,
28 | title: ' ',
29 | render: (h, params) => {
30 | const status = params.row.my_status
31 | if (status === null || status === undefined) {
32 | return undefined
33 | }
34 | return h('Icon', {
35 | props: {
36 | type: status === 0 ? 'checkmark-round' : 'minus-round',
37 | size: '16'
38 | },
39 | style: {
40 | color: status === 0 ? '#19be6b' : '#ed3f14'
41 | }
42 | })
43 | }
44 | })
45 | this.statusColumn = true
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/frontend/src/pages/oj/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | OnlineJudge
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/frontend/src/pages/oj/router/index.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import VueRouter from 'vue-router'
3 | import routes from './routes'
4 | import storage from '@/utils/storage'
5 | import { STORAGE_KEY } from '@/utils/constants'
6 | import { sync } from 'vuex-router-sync'
7 | import store, { types } from '@/store'
8 |
9 | Vue.use(VueRouter)
10 |
11 | const router = new VueRouter({
12 | mode: 'history',
13 | scrollBehavior (to, from, savedPosition) {
14 | if (savedPosition) {
15 | return savedPosition
16 | } else {
17 | return { x: 0, y: 0 }
18 | }
19 | },
20 | routes
21 | })
22 |
23 | // 全局身份确认
24 | router.beforeEach((to, from, next) => {
25 | if (to.matched.some(record => record.meta.requiresAuth)) {
26 | if (!storage.get(STORAGE_KEY.AUTHED)) {
27 | Vue.prototype.$error('Please login first')
28 | store.commit(types.CHANGE_MODAL_STATUS, { mode: 'login', visible: true })
29 | next({
30 | name: 'home'
31 | })
32 | } else {
33 | next()
34 | }
35 | } else {
36 | next()
37 | }
38 | })
39 |
40 | sync(store, router)
41 |
42 | export default router
43 |
--------------------------------------------------------------------------------
/frontend/src/pages/oj/views/user/EmailAuth.vue:
--------------------------------------------------------------------------------
1 |
23 |
--------------------------------------------------------------------------------
/frontend/src/pages/oj/views/user/Logout.vue:
--------------------------------------------------------------------------------
1 |
14 |
--------------------------------------------------------------------------------
/frontend/src/pages/professor/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
13 |
14 |
31 |
--------------------------------------------------------------------------------
/frontend/src/pages/professor/components/KatexEditor.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Put LaTeX in the following format to use LaTeX in our editor.
5 | \( ... \), $$ ... $$, \[ ... \]
6 | For example, \( \pm\sqrt{a^2 + b^2} \) or $$ \pm\sqrt{a^2 + b^2} $$
7 |
8 |
9 | Input
10 |
11 |
16 |
17 |
18 | Output
19 |
20 |
21 |
22 |
23 |
24 |
34 |
35 |
40 |
--------------------------------------------------------------------------------
/frontend/src/pages/professor/components/btn/Cancel.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | Cancel
4 |
5 |
6 |
11 |
16 |
--------------------------------------------------------------------------------
/frontend/src/pages/professor/components/btn/IconBtn.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
13 |
14 |
29 |
--------------------------------------------------------------------------------
/frontend/src/pages/professor/components/btn/Save.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | Save
4 |
5 |
6 |
11 |
16 |
--------------------------------------------------------------------------------
/frontend/src/pages/professor/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | SKKU Coding platform professor
5 |
6 |
7 |
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/frontend/src/pages/professor/views/index.js:
--------------------------------------------------------------------------------
1 | import Dashboard from './general/Dashboard.vue'
2 | import CourseDashboard from './general/CourseDashboard.vue'
3 | import Problem from './problem/Problem.vue'
4 | import AssignmentList from './assignment/AssignmentList.vue'
5 | import Login from './general/Login.vue'
6 | import Home from './Home.vue'
7 | import QnA from './qna/QnA.vue'
8 | import ProblemGrade from './problem/ProblemGrade.vue'
9 | import CourseBookmark from './general/CourseBookmark.vue'
10 |
11 | export {
12 | Dashboard, CourseDashboard, Problem, AssignmentList, Login, Home, QnA, ProblemGrade, CourseBookmark
13 | }
14 |
--------------------------------------------------------------------------------
/frontend/src/pages/professor/views/qna/QnA.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
23 |
24 |
33 |
34 |
35 |
55 |
56 |
58 |
--------------------------------------------------------------------------------
/frontend/src/plugins/highlight.js:
--------------------------------------------------------------------------------
1 | import hljs from 'highlight.js/lib/highlight'
2 | // import cpp from 'highlight.js/lib/languages/cpp'
3 | // import python from 'highlight.js/lib/languages/python'
4 | // import java from 'highlight.js/lib/languages/java'
5 | import 'highlight.js/styles/atom-one-light.css'
6 | // hljs.registerLanguage('cpp', cpp)
7 | // hljs.registerLanguage('java', java)
8 | // hljs.registerLanguage('python', python)
9 | export default {
10 | install (Vue, options) {
11 | Vue.directive('highlight', {
12 | deep: true,
13 | bind: function (el, binding) {
14 | // on first bind, highlight all targets
15 | Array.from(el.querySelectorAll('code')).forEach((target) => {
16 | // if a value is directly assigned to the directive, use this
17 | // instead of the element content.
18 | if (binding.value) {
19 | target.textContent = binding.value
20 | }
21 | hljs.highlightBlock(target)
22 | })
23 | },
24 | componentUpdated: function (el, binding) {
25 | // after an update, re-fill the content and then highlight
26 | Array.from(el.querySelectorAll('code')).forEach((target) => {
27 | if (binding.value) {
28 | target.textContent = binding.value
29 | }
30 | hljs.highlightBlock(target)
31 | })
32 | }
33 | })
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/frontend/src/store/modules/group.js:
--------------------------------------------------------------------------------
1 | import types from '../types'
2 | import api from '@oj/api'
3 |
4 | const state = {
5 | groups: {
6 | admin_groups: [],
7 | groups: [],
8 | other_groups: []
9 | }
10 | }
11 |
12 | const getters = {
13 | groups: state => state.groups.groups || [],
14 | adminGroups: state => state.groups.admin_groups || [],
15 | otherGroups: state => state.groups.other_groups || []
16 | }
17 |
18 | const mutations = {
19 | [types.CHANGE_GROUP_LIST] (state, payload) {
20 | state.groups = payload.groups
21 | }
22 | }
23 |
24 | const actions = {
25 | async getGroupList ({ commit, rootState }) {
26 | try {
27 | const res = await api.getGroupList()
28 | commit(types.CHANGE_GROUP_LIST, { groups: res.data.data })
29 | return res
30 | } catch (err) {
31 | commit(types.CHANGE_GROUP_LIST, { groups: [] })
32 | throw err
33 | }
34 | }
35 | }
36 |
37 | export default {
38 | state,
39 | mutations,
40 | getters,
41 | actions
42 | }
43 |
--------------------------------------------------------------------------------
/frontend/src/store/modules/user.js:
--------------------------------------------------------------------------------
1 | import types from '../types'
2 | import api from '@oj/api'
3 | import storage from '@/utils/storage'
4 | import { STORAGE_KEY, USER_TYPE, PROBLEM_PERMISSION } from '@/utils/constants'
5 |
6 | const state = {
7 | profile: {}
8 | }
9 |
10 | const getters = {
11 | user: state => state.profile.user || {},
12 | profile: state => state.profile,
13 | isAuthenticated: (state, getters) => {
14 | return !!getters.user.id
15 | },
16 | isAdminRole: (state, getters) => {
17 | return getters.user.admin_type === USER_TYPE.ADMIN ||
18 | getters.user.admin_type === USER_TYPE.SUPER_ADMIN
19 | },
20 | isSuperAdmin: (state, getters) => {
21 | return getters.user.admin_type === USER_TYPE.SUPER_ADMIN
22 | },
23 | hasProblemPermission: (state, getters) => {
24 | return getters.user.problem_permission !== PROBLEM_PERMISSION.NONE
25 | }
26 | }
27 |
28 | const mutations = {
29 | [types.CHANGE_PROFILE] (state, { profile }) {
30 | state.profile = profile
31 | storage.set(STORAGE_KEY.AUTHED, !!profile.user)
32 | }
33 | }
34 |
35 | const actions = {
36 | async getProfile ({ commit }) {
37 | try {
38 | const res = await api.getUserInfo()
39 | commit(types.CHANGE_PROFILE, {
40 | profile: res.data.data || {}
41 | })
42 | } catch (err) {
43 | }
44 | },
45 | clearProfile ({ commit }) {
46 | commit(types.CHANGE_PROFILE, {
47 | profile: {}
48 | })
49 | // storage.clear()
50 | for (const key in localStorage) {
51 | if (key.indexOf('problemCode') === -1) {
52 | storage.remove(key)
53 | }
54 | }
55 | }
56 | }
57 |
58 | export default {
59 | state,
60 | getters,
61 | actions,
62 | mutations
63 | }
64 |
--------------------------------------------------------------------------------
/frontend/src/store/types.js:
--------------------------------------------------------------------------------
1 | function keyMirror (obj) {
2 | if (obj instanceof Object) {
3 | const _obj = Object.assign({}, obj)
4 | const _keyArray = Object.keys(obj)
5 | _keyArray.forEach(key => {
6 | _obj[key] = key
7 | })
8 | return _obj
9 | }
10 | }
11 |
12 | export default keyMirror({
13 | CHANGE_PROFILE: null,
14 | CHANGE_MODAL_STATUS: null,
15 | UPDATE_WEBSITE_CONF: null,
16 |
17 | NOW: null,
18 | NOW_ADD_1S: null,
19 | CHANGE_CONTEST: null,
20 | CHANGE_CONTEST_PROBLEMS: null,
21 | CHANGE_CONTEST_ITEM_VISIBLE: null,
22 | CHANGE_RANK_FORCE_UPDATE: null,
23 | CHANGE_CONTEST_RANK_LIMIT: null,
24 | CONTEST_ACCESS: null,
25 | CLEAR_CONTEST: null,
26 |
27 | CHANGE_ASSIGNMENT: null,
28 | CHANGE_ASSIGNMENTLIST: null,
29 | CHANGE_ASSIGNMENT_PROBLEMS: null,
30 |
31 | CHANGE_GROUP_LIST: null
32 | })
33 |
--------------------------------------------------------------------------------
/frontend/src/styles/tailwind.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
--------------------------------------------------------------------------------
/frontend/src/styles/tiptapview.scss:
--------------------------------------------------------------------------------
1 | ul,
2 | ol {
3 | padding: 0 1rem;
4 | }
5 | code {
6 | background-color: rgba(#616161, 0.1);
7 | }
8 | pre {
9 | background: #0D0D0D;
10 | color: #FFF;
11 | font-family: 'JetBrainsMono', monospace;
12 | padding: 0.75rem 1rem;
13 | border-radius: 0.5rem;
14 |
15 | code {
16 | color: inherit;
17 | padding: 0;
18 | background: none;
19 | font-size: 0.8rem;
20 | }
21 | }
22 | img {
23 | max-width: 100%;
24 | height: auto;
25 | }
26 | blockquote {
27 | padding-left: 1rem;
28 | border-left: 2px solid rgba(#0D0D0D, 0.1);
29 | }
30 | hr {
31 | border: none;
32 | border-top: 2px solid rgba(#0D0D0D, 0.1);
33 | }
--------------------------------------------------------------------------------
/frontend/src/utils/filters.js:
--------------------------------------------------------------------------------
1 | import moment from 'moment'
2 | import utils from './utils'
3 | import time from './time'
4 |
5 | // 友好显示时间
6 | function fromNow (time) {
7 | return moment(time * 3).fromNow()
8 | }
9 |
10 | export default {
11 | submissionMemory: utils.submissionMemoryFormat,
12 | submissionTime: utils.submissionTimeFormat,
13 | localtime: time.utcToLocal,
14 | fromNow: fromNow
15 | }
16 |
--------------------------------------------------------------------------------
/frontend/src/utils/storage.js:
--------------------------------------------------------------------------------
1 | const localStorage = window.localStorage
2 |
3 | export default {
4 | name: 'storage',
5 |
6 | /**
7 | * save value(Object) to key
8 | * @param {string} key 键
9 | * @param {Object} value 值
10 | */
11 | set (key, value) {
12 | localStorage.setItem(key, JSON.stringify(value))
13 | },
14 |
15 | /**
16 | * get value(Object) by key
17 | * @param {string} key 键
18 | * @return {Object}
19 | */
20 | get (key) {
21 | return JSON.parse(localStorage.getItem(key)) || null
22 | },
23 |
24 | /**
25 | * remove key from localStorage
26 | * @param {string} key 键
27 | */
28 | remove (key) {
29 | localStorage.removeItem(key)
30 | },
31 | /**
32 | * clear all
33 | */
34 | clear () {
35 | localStorage.clear()
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/frontend/src/utils/time.js:
--------------------------------------------------------------------------------
1 | import moment from 'moment'
2 |
3 | // convert utc time to localtime
4 | function utcToLocal (utcDt, format = 'YYYY-M-D HH:mm:ss') {
5 | return moment.utc(utcDt).local().format(format)
6 | }
7 |
8 | // get duration from startTime to endTime, return like 3 days, 2 hours, one year ..
9 | function duration (startTime, endTime) {
10 | const start = moment(startTime)
11 | const end = moment(endTime)
12 | const duration = moment.duration(start.diff(end, 'seconds'), 'seconds')
13 | if (duration.days() !== 0) {
14 | return duration.humanize()
15 | }
16 | return Math.abs(duration.asHours().toFixed(1)) + ' hours'
17 | }
18 |
19 | function secondFormat (seconds) {
20 | const m = moment.duration(seconds, 'seconds')
21 | return Math.floor(m.asHours()) + ':' + m.minutes() + ':' + m.seconds()
22 | }
23 |
24 | export default {
25 | utcToLocal: utcToLocal,
26 | duration: duration,
27 | secondFormat: secondFormat
28 | }
29 |
--------------------------------------------------------------------------------
/frontend/tailwind.config.js:
--------------------------------------------------------------------------------
1 | const defaultTheme = require('tailwindcss/defaultTheme')
2 |
3 | module.exports = {
4 | content: ['./index.html', './src/**/*.{vue,js,jsx}'],
5 | darkMode: false,
6 | theme: {
7 | extend: {
8 | fontFamily: {
9 | sans: [
10 | 'Manrope',
11 | ...defaultTheme.fontFamily.sans
12 | ]
13 | }
14 | },
15 | colors: {
16 | white: '#ffffff',
17 | green: '#8DC63F',
18 | blue: '#3391E5',
19 | black: '#000000',
20 | transparent: '#0000',
21 | level: {
22 | level1: '#CC99C9',
23 | level2: '#9EC1CF',
24 | level3: '#A1F2C2',
25 | level4: '#B8FF81',
26 | level5: '#F3EC53',
27 | level6: '#FEB144',
28 | level7: '#FF6663'
29 | },
30 | text: {
31 | title: '#7C7A7B',
32 | content: '#495057'
33 | },
34 | table: {
35 | header: '#F9F9FA',
36 | hover: '#072B604D',
37 | border: '#e0e2e3'
38 | }
39 | }
40 | },
41 | plugins: []
42 | }
43 |
--------------------------------------------------------------------------------