├── api ├── __init__.py ├── views │ ├── __init__.py │ ├── pagination.py │ ├── user.py │ ├── tag.py │ ├── problemlist.py │ ├── problem.py │ └── submission.py ├── migrations │ └── __init__.py └── examples.py ├── blog ├── __init__.py ├── migrations │ └── __init__.py ├── urls.py └── forms.py ├── contest ├── reward.py ├── __init__.py └── migrations │ ├── __init__.py │ ├── 0002_contestinvitation_availability.py │ └── 0003_auto_20211215_1726.py ├── eoj3 ├── __init__.py ├── local_settings.example.py ├── captcha.py └── wsgi.py ├── home ├── __init__.py ├── migrations │ └── __init__.py └── api_views.py ├── paste ├── __init__.py ├── migrations │ └── __init__.py ├── urls.py ├── forms.py └── models.py ├── tests ├── __init__.py └── views.py ├── account ├── __init__.py ├── migrations │ ├── __init__.py │ └── 0002_auto_20210411_1442.py ├── admin.py ├── color.py ├── urls.py └── permissions.py ├── backstage ├── __init__.py ├── blog │ ├── __init__.py │ └── views.py ├── email │ └── __init__.py ├── server │ ├── __init__.py │ └── forms.py ├── site │ ├── __init__.py │ ├── forms.py │ └── views.py ├── account │ ├── __init__.py │ └── forms.py ├── contest │ └── __init__.py ├── migrations │ └── __init__.py └── problem │ └── __init__.py ├── commons └── __init__.py ├── dispatcher ├── __init__.py ├── migrations │ └── __init__.py ├── utils.py └── models.py ├── filemanager ├── __init__.py ├── migrations │ └── __init__.py └── urls.py ├── migrate ├── __init__.py ├── migrations │ └── __init__.py ├── forms.py └── models.py ├── polygon ├── __init__.py ├── contest │ └── __init__.py ├── package │ └── __init__.py ├── problem2 │ ├── __init__.py │ ├── views │ │ └── __init__.py │ └── utils.py ├── tests │ └── __init__.py ├── migrations │ └── __init__.py ├── assets │ └── jplag-2.11.9_SNAPSHOT.jar ├── templates │ └── polygon │ │ ├── problem2 │ │ ├── case │ │ │ ├── update.jinja2 │ │ │ └── create.jinja2 │ │ ├── simple_form.jinja2 │ │ ├── status.jinja2 │ │ ├── template │ │ │ └── preview.jinja2 │ │ ├── package.jinja2 │ │ ├── program │ │ │ └── preview.jinja2 │ │ └── statement │ │ │ └── preview.jinja2 │ │ ├── contest │ │ ├── ghost_import.jinja2 │ │ ├── status.jinja2 │ │ ├── base.jinja2 │ │ └── anticheat.jinja2 │ │ └── runs.jinja2 ├── base_views.py └── urls.py ├── problem ├── __init__.py ├── migrations │ ├── __init__.py │ └── 0002_auto_20201222_1327.py ├── models │ ├── __init__.py │ ├── tag.py │ ├── feedback.py │ ├── skill.py │ └── status.py ├── testlib │ ├── checker │ │ ├── pointscmp.cpp │ │ ├── icmp.cpp │ │ ├── acmp.cpp │ │ ├── rcmp.cpp │ │ ├── dcmp.cpp │ │ ├── rncmp.cpp │ │ ├── yesno.cpp │ │ ├── rcmp6.cpp │ │ ├── rcmp4.cpp │ │ ├── rcmp9.cpp │ │ ├── fcmp.cpp │ │ ├── hcmp.cpp │ │ ├── wcmp.cpp │ │ └── lcmp.cpp │ ├── validator │ │ ├── ival.cpp │ │ ├── sval.cpp │ │ ├── nval.cpp │ │ ├── validate-using-testset-and-group.cpp │ │ ├── bipartite-graph-validator.cpp │ │ └── undirected-graph-validator.cpp │ ├── generator │ │ ├── bgen.cpp │ │ ├── igen.cpp │ │ ├── sgen.cpp │ │ ├── swgen.cpp │ │ ├── multigen.cpp │ │ ├── iwgen.cpp │ │ ├── gen-rooted-tree-graph.cpp │ │ └── gen-tree-graph.cpp │ ├── interactor │ │ └── interactor-a-plus-b.cpp │ └── pack.py └── urls.py ├── scripts ├── __init__.py └── invalidate_problem_stats.py ├── submission ├── __init__.py ├── migrations │ └── __init__.py ├── assets │ └── template.tex └── util.py ├── templates ├── test.jinja2 ├── blank.jinja2 ├── captcha │ ├── hidden_field.html │ ├── text_field.html │ ├── field.html │ └── image.html ├── api │ └── problem.jinja2 ├── account │ ├── preference.jinja2 │ ├── security.jinja2 │ ├── password_reset.jinja2 │ ├── password_reset_done.jinja2 │ ├── password_reset_confirm.jinja2 │ ├── password_reset_email.jinja2 │ ├── migrate.jinja2 │ ├── base.jinja2 │ └── profile.jinja2 ├── comments │ └── comment_reply.jinja2 ├── components │ ├── message.jinja2 │ ├── timeanddate_link.jinja2 │ ├── post_link.jinja2 │ ├── username_display.jinja2 │ ├── delete_confirmation.jinja2 │ ├── search_user.jinja2 │ ├── rsa_encrypt.jinja2 │ ├── modal.jinja2 │ ├── profile_card.jinja2 │ ├── blog_preview.jinja2 │ ├── footer.jinja2 │ └── pagination.jinja2 ├── problem │ ├── status.jinja2 │ ├── detail │ │ ├── discussion.jinja2 │ │ ├── base.jinja2 │ │ └── tag.jinja2 │ ├── source.jinja2 │ ├── reward.jinja2 │ ├── standings.jinja2 │ ├── base.jinja2 │ ├── archive.jinja2 │ └── feedback │ │ └── compare.jinja2 ├── backstage │ ├── index.jinja2 │ ├── email │ │ ├── create.jinja2 │ │ └── list.jinja2 │ ├── server │ │ ├── server_add.jinja2 │ │ └── server_edit.jinja2 │ ├── problem │ │ ├── skill_edit.jinja2 │ │ ├── tags_edit.jinja2 │ │ ├── source_edit.jinja2 │ │ └── skill.jinja2 │ ├── account │ │ ├── school_form.jinja2 │ │ └── school.jinja2 │ ├── site │ │ └── site.jinja2 │ ├── contest │ │ └── contest.jinja2 │ ├── base.jinja2 │ └── blog │ │ └── blog.jinja2 ├── contest │ ├── status.jinja2 │ ├── contest_pdf_statement_notice.jinja2 │ ├── activity │ │ ├── add.jinja2 │ │ ├── admin_add.jinja2 │ │ ├── admin_update.jinja2 │ │ ├── edit.jinja2 │ │ ├── school_form.jinja2 │ │ ├── confirm_complete.jinja2 │ │ ├── confirmation.jinja2 │ │ └── list.jinja2 │ ├── standings_penalty_detail.jinja2 │ ├── reward.jinja2 │ ├── my_status.jinja2 │ ├── contest_ratings.jinja2 │ ├── balloon_detail.jinja2 │ ├── problem.jinja2 │ └── balloon.jinja2 ├── support │ └── feedback.jinja2 ├── blog │ ├── blog_add.jinja2 │ ├── blog_edit.jinja2 │ ├── reward_list.jinja2 │ └── generic.jinja2 ├── taggraph.jinja2 ├── register.jinja2 ├── error │ ├── closed.jinja2 │ ├── 404.jinja2 │ ├── 500.jinja2 │ └── 403.jinja2 ├── login.jinja2 ├── report_download.jinja2 ├── update_log.jinja2 ├── paste │ └── detail.jinja2 ├── print │ └── admin.jinja2 ├── notification │ └── list.jinja2 ├── submission.jinja2 └── base.jinja2 ├── notification ├── __init__.py ├── migrations │ └── __init__.py ├── context_processors │ ├── __init__.py │ └── notification_processor.py ├── urls.py └── views.py ├── static ├── less │ ├── polygon.less │ ├── username.less │ ├── simplemde.less │ ├── app.less │ └── layout.less ├── css │ └── fake.css ├── .bowerrc ├── favicon.ico ├── image │ ├── bg │ │ ├── 1.jpg │ │ ├── 2.jpg │ │ ├── 3.jpg │ │ ├── 4.jpg │ │ ├── 5.jpg │ │ ├── 6.jpg │ │ └── 7.jpg │ ├── error │ │ ├── 403.png │ │ ├── 404.png │ │ ├── 500.png │ │ └── smile.png │ └── avatar │ │ ├── large │ │ ├── ade.jpg │ │ ├── eve.png │ │ ├── joe.jpg │ │ ├── nan.jpg │ │ ├── nom.jpg │ │ ├── tom.jpg │ │ ├── zoe.jpg │ │ ├── cassie.png │ │ ├── chris.jpg │ │ ├── daniel.jpg │ │ ├── elliot.jpg │ │ ├── elyse.png │ │ ├── helen.jpg │ │ ├── jenny.jpg │ │ ├── justen.jpg │ │ ├── kristy.png │ │ ├── laura.jpg │ │ ├── lena.png │ │ ├── mark.png │ │ ├── matt.jpg │ │ ├── molly.png │ │ ├── rachel.png │ │ ├── steve.jpg │ │ ├── stevie.jpg │ │ ├── lindsay.png │ │ ├── matthew.png │ │ ├── patrick.png │ │ ├── veronika.jpg │ │ └── christian.jpg │ │ └── small │ │ ├── ade.jpg │ │ ├── eve.png │ │ ├── joe.jpg │ │ ├── nan.jpg │ │ ├── nom.jpg │ │ ├── tom.jpg │ │ ├── zoe.jpg │ │ ├── cassie.png │ │ ├── chris.jpg │ │ ├── daniel.jpg │ │ ├── elliot.jpg │ │ ├── elyse.png │ │ ├── helen.jpg │ │ ├── jenny.jpg │ │ ├── justen.jpg │ │ ├── kristy.png │ │ ├── laura.jpg │ │ ├── lena.png │ │ ├── mark.png │ │ ├── matt.jpg │ │ ├── molly.png │ │ ├── rachel.png │ │ ├── steve.jpg │ │ ├── stevie.jpg │ │ ├── lindsay.png │ │ ├── matthew.png │ │ ├── patrick.png │ │ ├── veronika.jpg │ │ └── christian.jpg ├── fonts │ ├── FontAwesome.otf │ ├── material-icons.woff2 │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ ├── fontawesome-webfont.woff2 │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.ttf │ ├── glyphicons-halflings-regular.woff │ └── glyphicons-halflings-regular.woff2 ├── js │ ├── backstage.js │ ├── markdown.js │ ├── longpoll.js │ ├── input.file.js │ └── countdown.js ├── bower.json ├── gulpfile.js └── package.json ├── utils ├── jinja2 │ ├── __init__.py │ └── tests.py ├── middleware │ ├── __init__.py │ ├── close_site_middleware.py │ └── globalrequestmiddleware.py ├── migrations │ ├── __init__.py │ └── 0001_initial.py ├── __init__.py ├── time.py ├── authentication.py ├── serve_nginx.py ├── tagging.py ├── detail_formatter.py ├── debug.py ├── url_formatter.py ├── csv_writer.py ├── multiple_choice_field.py ├── email.py ├── markdown3 │ ├── __init__.py │ ├── semantic.py │ └── mdx_downheader.py ├── language.py ├── upload.py ├── permission.py └── rsa_gen.py ├── locale └── humanize │ └── zh_Hans │ └── LC_MESSAGES │ └── django.mo ├── .gitignore ├── .dockerignore ├── README.md ├── .github └── workflows │ ├── docker-image.yml │ └── pythonapp.yml ├── Dockerfile ├── manage.py ├── pylintrc ├── LICENSE └── docs └── Development.md /api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /blog/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /contest/reward.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /eoj3/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /home/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /paste/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /account/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /api/views/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backstage/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /commons/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /contest/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dispatcher/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /filemanager/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /migrate/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /polygon/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /problem/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /scripts/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /submission/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/test.jinja2: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /api/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backstage/blog/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backstage/email/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backstage/server/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backstage/site/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /blog/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /home/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /notification/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /paste/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /polygon/contest/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /polygon/package/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /polygon/problem2/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /polygon/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/less/polygon.less: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /utils/jinja2/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /utils/middleware/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /utils/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /account/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backstage/account/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backstage/contest/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backstage/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backstage/problem/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /contest/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /dispatcher/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /migrate/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /polygon/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /problem/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/css/fake.css: -------------------------------------------------------------------------------- 1 | p { 2 | 3 | } -------------------------------------------------------------------------------- /submission/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /filemanager/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /notification/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /polygon/problem2/views/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /templates/blank.jinja2: -------------------------------------------------------------------------------- 1 |

Success

-------------------------------------------------------------------------------- /notification/context_processors/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /eoj3/local_settings.example.py: -------------------------------------------------------------------------------- 1 | DEBUG = True 2 | -------------------------------------------------------------------------------- /static/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/favicon.ico -------------------------------------------------------------------------------- /static/image/bg/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/bg/1.jpg -------------------------------------------------------------------------------- /static/image/bg/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/bg/2.jpg -------------------------------------------------------------------------------- /static/image/bg/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/bg/3.jpg -------------------------------------------------------------------------------- /static/image/bg/4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/bg/4.jpg -------------------------------------------------------------------------------- /static/image/bg/5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/bg/5.jpg -------------------------------------------------------------------------------- /static/image/bg/6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/bg/6.jpg -------------------------------------------------------------------------------- /static/image/bg/7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/bg/7.jpg -------------------------------------------------------------------------------- /static/image/error/403.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/error/403.png -------------------------------------------------------------------------------- /static/image/error/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/error/404.png -------------------------------------------------------------------------------- /static/image/error/500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/error/500.png -------------------------------------------------------------------------------- /dispatcher/utils.py: -------------------------------------------------------------------------------- 1 | def is_success_response(response): 2 | return response.get('status') == 'received' 3 | -------------------------------------------------------------------------------- /static/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /static/image/error/smile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/error/smile.png -------------------------------------------------------------------------------- /templates/captcha/hidden_field.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /account/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import User 3 | 4 | admin.site.register(User) 5 | -------------------------------------------------------------------------------- /static/fonts/material-icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/fonts/material-icons.woff2 -------------------------------------------------------------------------------- /static/image/avatar/large/ade.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/large/ade.jpg -------------------------------------------------------------------------------- /static/image/avatar/large/eve.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/large/eve.png -------------------------------------------------------------------------------- /static/image/avatar/large/joe.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/large/joe.jpg -------------------------------------------------------------------------------- /static/image/avatar/large/nan.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/large/nan.jpg -------------------------------------------------------------------------------- /static/image/avatar/large/nom.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/large/nom.jpg -------------------------------------------------------------------------------- /static/image/avatar/large/tom.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/large/tom.jpg -------------------------------------------------------------------------------- /static/image/avatar/large/zoe.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/large/zoe.jpg -------------------------------------------------------------------------------- /static/image/avatar/small/ade.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/small/ade.jpg -------------------------------------------------------------------------------- /static/image/avatar/small/eve.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/small/eve.png -------------------------------------------------------------------------------- /static/image/avatar/small/joe.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/small/joe.jpg -------------------------------------------------------------------------------- /static/image/avatar/small/nan.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/small/nan.jpg -------------------------------------------------------------------------------- /static/image/avatar/small/nom.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/small/nom.jpg -------------------------------------------------------------------------------- /static/image/avatar/small/tom.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/small/tom.jpg -------------------------------------------------------------------------------- /static/image/avatar/small/zoe.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/small/zoe.jpg -------------------------------------------------------------------------------- /static/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /static/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /static/image/avatar/large/cassie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/large/cassie.png -------------------------------------------------------------------------------- /static/image/avatar/large/chris.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/large/chris.jpg -------------------------------------------------------------------------------- /static/image/avatar/large/daniel.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/large/daniel.jpg -------------------------------------------------------------------------------- /static/image/avatar/large/elliot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/large/elliot.jpg -------------------------------------------------------------------------------- /static/image/avatar/large/elyse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/large/elyse.png -------------------------------------------------------------------------------- /static/image/avatar/large/helen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/large/helen.jpg -------------------------------------------------------------------------------- /static/image/avatar/large/jenny.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/large/jenny.jpg -------------------------------------------------------------------------------- /static/image/avatar/large/justen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/large/justen.jpg -------------------------------------------------------------------------------- /static/image/avatar/large/kristy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/large/kristy.png -------------------------------------------------------------------------------- /static/image/avatar/large/laura.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/large/laura.jpg -------------------------------------------------------------------------------- /static/image/avatar/large/lena.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/large/lena.png -------------------------------------------------------------------------------- /static/image/avatar/large/mark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/large/mark.png -------------------------------------------------------------------------------- /static/image/avatar/large/matt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/large/matt.jpg -------------------------------------------------------------------------------- /static/image/avatar/large/molly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/large/molly.png -------------------------------------------------------------------------------- /static/image/avatar/large/rachel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/large/rachel.png -------------------------------------------------------------------------------- /static/image/avatar/large/steve.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/large/steve.jpg -------------------------------------------------------------------------------- /static/image/avatar/large/stevie.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/large/stevie.jpg -------------------------------------------------------------------------------- /static/image/avatar/small/cassie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/small/cassie.png -------------------------------------------------------------------------------- /static/image/avatar/small/chris.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/small/chris.jpg -------------------------------------------------------------------------------- /static/image/avatar/small/daniel.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/small/daniel.jpg -------------------------------------------------------------------------------- /static/image/avatar/small/elliot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/small/elliot.jpg -------------------------------------------------------------------------------- /static/image/avatar/small/elyse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/small/elyse.png -------------------------------------------------------------------------------- /static/image/avatar/small/helen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/small/helen.jpg -------------------------------------------------------------------------------- /static/image/avatar/small/jenny.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/small/jenny.jpg -------------------------------------------------------------------------------- /static/image/avatar/small/justen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/small/justen.jpg -------------------------------------------------------------------------------- /static/image/avatar/small/kristy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/small/kristy.png -------------------------------------------------------------------------------- /static/image/avatar/small/laura.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/small/laura.jpg -------------------------------------------------------------------------------- /static/image/avatar/small/lena.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/small/lena.png -------------------------------------------------------------------------------- /static/image/avatar/small/mark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/small/mark.png -------------------------------------------------------------------------------- /static/image/avatar/small/matt.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/small/matt.jpg -------------------------------------------------------------------------------- /static/image/avatar/small/molly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/small/molly.png -------------------------------------------------------------------------------- /static/image/avatar/small/rachel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/small/rachel.png -------------------------------------------------------------------------------- /static/image/avatar/small/steve.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/small/steve.jpg -------------------------------------------------------------------------------- /static/image/avatar/small/stevie.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/small/stevie.jpg -------------------------------------------------------------------------------- /problem/models/__init__.py: -------------------------------------------------------------------------------- 1 | from .problem import * 2 | from .skill import * 3 | from .tag import * 4 | from .status import * 5 | -------------------------------------------------------------------------------- /static/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /static/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /static/image/avatar/large/lindsay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/large/lindsay.png -------------------------------------------------------------------------------- /static/image/avatar/large/matthew.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/large/matthew.png -------------------------------------------------------------------------------- /static/image/avatar/large/patrick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/large/patrick.png -------------------------------------------------------------------------------- /static/image/avatar/large/veronika.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/large/veronika.jpg -------------------------------------------------------------------------------- /static/image/avatar/small/lindsay.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/small/lindsay.png -------------------------------------------------------------------------------- /static/image/avatar/small/matthew.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/small/matthew.png -------------------------------------------------------------------------------- /static/image/avatar/small/patrick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/small/patrick.png -------------------------------------------------------------------------------- /static/image/avatar/small/veronika.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/small/veronika.jpg -------------------------------------------------------------------------------- /polygon/assets/jplag-2.11.9_SNAPSHOT.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/polygon/assets/jplag-2.11.9_SNAPSHOT.jar -------------------------------------------------------------------------------- /static/image/avatar/large/christian.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/large/christian.jpg -------------------------------------------------------------------------------- /static/image/avatar/small/christian.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/image/avatar/small/christian.jpg -------------------------------------------------------------------------------- /templates/api/problem.jinja2: -------------------------------------------------------------------------------- 1 | {% from 'components/problem.jinja2' import problem_view with context %} 2 | {{ problem_view(problem, False) }} -------------------------------------------------------------------------------- /locale/humanize/zh_Hans/LC_MESSAGES/django.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/locale/humanize/zh_Hans/LC_MESSAGES/django.mo -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/F0RE1GNERS/eoj3/HEAD/static/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /static/less/username.less: -------------------------------------------------------------------------------- 1 | a.legendary:first-letter 2 | { 3 | color: #000000; 4 | } 5 | 6 | a.legendary 7 | { 8 | color: #DB2828; 9 | } 10 | -------------------------------------------------------------------------------- /templates/account/preference.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'account/base.jinja2' %} 2 | {% block account_setting %} 3 | {% include 'components/form.jinja2' %} 4 | {% endblock %} -------------------------------------------------------------------------------- /utils/__init__.py: -------------------------------------------------------------------------------- 1 | from shortuuid import ShortUUID 2 | 3 | random_gen = ShortUUID() 4 | 5 | 6 | def random_string(length=24): 7 | return random_gen.random(length) 8 | -------------------------------------------------------------------------------- /polygon/templates/polygon/problem2/case/update.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'polygon/problem2/base.jinja2' %} 2 | 3 | {% block problem_content %} 4 | {% include 'components/form.jinja2' %} 5 | {% endblock %} 6 | -------------------------------------------------------------------------------- /polygon/templates/polygon/problem2/simple_form.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'polygon/problem2/base.jinja2' %} 2 | 3 | {% block problem_content %} 4 | {% include 'components/form.jinja2' %} 5 | {% endblock %} 6 | -------------------------------------------------------------------------------- /utils/time.py: -------------------------------------------------------------------------------- 1 | from django.conf import settings 2 | 3 | 4 | def datetime_display(time): 5 | try: 6 | return time.strftime(settings.DATETIME_FORMAT_TEMPLATE) 7 | except: 8 | return "" 9 | -------------------------------------------------------------------------------- /api/views/pagination.py: -------------------------------------------------------------------------------- 1 | from rest_framework.pagination import LimitOffsetPagination 2 | 3 | 4 | class StandardResultsSetPagination(LimitOffsetPagination): 5 | default_limit = 10 6 | max_limit = 100 7 | -------------------------------------------------------------------------------- /polygon/templates/polygon/contest/ghost_import.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'polygon/contest/base.jinja2' %} 2 | 3 | {% block contest_content %} 4 | 5 | {% include 'components/form.jinja2' %} 6 | 7 | {% endblock %} 8 | -------------------------------------------------------------------------------- /templates/captcha/text_field.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /templates/comments/comment_reply.jinja2: -------------------------------------------------------------------------------- 1 | 2 | 你当前正在回复 博客/题目 3 | 4 | 5 | {% include 'components/form.jinja2' %} -------------------------------------------------------------------------------- /utils/authentication.py: -------------------------------------------------------------------------------- 1 | from rest_framework.authentication import SessionAuthentication 2 | 3 | 4 | class UnsafeSessionAuthentication(SessionAuthentication): 5 | def enforce_csrf(self, request): 6 | return 7 | -------------------------------------------------------------------------------- /utils/jinja2/tests.py: -------------------------------------------------------------------------------- 1 | from django_jinja import library 2 | 3 | from account.permissions import is_admin_or_root 4 | 5 | 6 | @library.test(name="admin") 7 | def is_admin(user): 8 | return is_admin_or_root(user) 9 | -------------------------------------------------------------------------------- /static/js/backstage.js: -------------------------------------------------------------------------------- 1 | $(".ui.progress").each(function () { 2 | if ($(this).data("progress")) { 3 | $(this).show(); 4 | $(this).progress({ 5 | percent: $(this).data("progress") 6 | }); 7 | } 8 | }); -------------------------------------------------------------------------------- /templates/components/message.jinja2: -------------------------------------------------------------------------------- 1 | {% for message in messages %} 2 |
3 | 4 | {{ message | safer | safe }} 5 |
6 | {% endfor %} 7 | -------------------------------------------------------------------------------- /backstage/account/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | 3 | from account.models import School 4 | 5 | 6 | class SchoolForm(forms.ModelForm): 7 | class Meta: 8 | model = School 9 | fields = "__all__" 10 | -------------------------------------------------------------------------------- /templates/problem/status.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'problem/base.jinja2' %} 2 | {% from 'components/status.jinja2' import status with context %} 3 | 4 | {% block problem_content %} 5 | 6 | {{ status() }} 7 | 8 | {% endblock %} 9 | -------------------------------------------------------------------------------- /templates/account/security.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'account/base.jinja2' %} 2 | 3 | {% block page_header %}安全设置{% endblock %} 4 | 5 | {% block account_setting %} 6 | 7 | {% include 'components/form.jinja2' %} 8 | 9 | {% endblock %} -------------------------------------------------------------------------------- /filemanager/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | 3 | from filemanager.views import FileManager 4 | 5 | app_name = "filemanager" 6 | 7 | urlpatterns = [ 8 | url(r'^$', FileManager.as_view(), name='index'), 9 | ] 10 | -------------------------------------------------------------------------------- /notification/context_processors/notification_processor.py: -------------------------------------------------------------------------------- 1 | def notification_processor(request): 2 | if request.user.is_authenticated: 3 | return {'notifications': request.user.notifications.unread()} 4 | else: 5 | return {} 6 | -------------------------------------------------------------------------------- /templates/backstage/index.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'backstage/base.jinja2' %} 2 | 3 | {% block content_header %} 4 | 首页 5 | {% endblock %} 6 | 7 | {% block backstage_content %} 8 | 9 |

EOJ 后台管理

10 | 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /templates/captcha/field.html: -------------------------------------------------------------------------------- 1 |
2 | {{ text_field }}{{ hidden_field }} 3 |
4 | {{ image }} 5 |
6 |
-------------------------------------------------------------------------------- /backstage/site/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | 3 | from utils.site_settings import SiteSettings 4 | 5 | 6 | class SiteSettingsForm(forms.ModelForm): 7 | class Meta: 8 | model = SiteSettings 9 | fields = '__all__' 10 | -------------------------------------------------------------------------------- /polygon/base_views.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.mixins import UserPassesTestMixin 2 | 3 | 4 | class PolygonBaseMixin(UserPassesTestMixin): 5 | 6 | def test_func(self): 7 | return self.request.user.is_authenticated and self.request.user.polygon_enabled 8 | -------------------------------------------------------------------------------- /templates/backstage/email/create.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'backstage/base.jinja2' %} 2 | 3 | {% block content_header %} 4 | 创建邮件 5 | {% endblock %} 6 | 7 | {% block backstage_content %} 8 | {% include 'components/form.jinja2' %} 9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /templates/backstage/server/server_add.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'backstage/base.jinja2' %} 2 | 3 | {% block content_header %} 4 | 增加判题服务器 5 | {% endblock %} 6 | 7 | {% block backstage_content %} 8 | {% include 'components/form.jinja2' %} 9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /templates/backstage/problem/skill_edit.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'backstage/base.jinja2' %} 2 | 3 | {% block content_header %} 4 | Edit Skill 5 | {% endblock %} 6 | 7 | {% block backstage_content %} 8 | {% include 'components/form.jinja2' %} 9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /templates/backstage/problem/tags_edit.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'backstage/base.jinja2' %} 2 | 3 | {% block content_header %} 4 | Edit Tag 5 | {% endblock %} 6 | 7 | {% block backstage_content %} 8 | {% include 'components/form.jinja2' %} 9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /templates/backstage/server/server_edit.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'backstage/base.jinja2' %} 2 | 3 | {% block content_header %} 4 | 修改判题机配置 5 | {% endblock %} 6 | 7 | {% block backstage_content %} 8 | {% include 'components/form.jinja2' %} 9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /templates/contest/status.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'contest/base.jinja2' %} 2 | {% from 'components/status.jinja2' import status with context %} 3 | 4 | {% block contest_title %}所有提交 - {% endblock %} 5 | {% block contest_content %} 6 | 7 | {{ status() }} 8 | 9 | {% endblock %} -------------------------------------------------------------------------------- /templates/backstage/account/school_form.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'backstage/base.jinja2' %} 2 | {% block content_header %} 3 | Schools 4 | {% endblock %} 5 | 6 | {% block backstage_content %} 7 | 8 | {% include 'components/form.jinja2' %} 9 | 10 | {% endblock %} 11 | -------------------------------------------------------------------------------- /templates/support/feedback.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block title %}Feedback - {% endblock %} 4 | {% block page_header %}Feedback{% endblock %} 5 | 6 | {% block content %} 7 | 8 | {% include 'components/form.jinja2' %} 9 | {% endblock %} 10 | -------------------------------------------------------------------------------- /templates/account/password_reset.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block title %}密码重置 - {% endblock %} 4 | 5 | {% block page_header %}密码重置{% endblock %} 6 | 7 | {% block content %} 8 | 9 | {% include 'components/form.jinja2' %} 10 | 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /templates/blog/blog_add.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block title %}Add a blog - {% endblock %} 4 | 5 | {% block page_header %}Add a blog{% endblock %} 6 | 7 | {% block content %} 8 | 9 | {% include 'components/form.jinja2' %} 10 | 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /templates/blog/blog_edit.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block title %}Edit my blog - {% endblock %} 4 | 5 | {% block page_header %}Edit my blog{% endblock %} 6 | 7 | {% block content %} 8 | 9 | {% include 'components/form.jinja2' %} 10 | 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /templates/contest/contest_pdf_statement_notice.jinja2: -------------------------------------------------------------------------------- 1 | {% if contest.pdf_statement %} 2 |
3 |
PDF 题面可用
4 |
你可以在这里下载。
5 |
6 | {% endif %} -------------------------------------------------------------------------------- /polygon/templates/polygon/problem2/status.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'polygon/problem2/base.jinja2' %} 2 | {% from 'components/status.jinja2' import status with context %} 3 | 4 | {% block problem_content %} 5 | 6 | {{ status(hide_problems=True, rejudge=True, show_ip=True) }} 7 | 8 | {% endblock %} -------------------------------------------------------------------------------- /templates/account/password_reset_done.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block title %}重置密码 - {% endblock %} 4 | 5 | {% block page_header %}重置密码{% endblock %} 6 | 7 | {% block content %} 8 | 9 |

10 | 密码重置邮件已经发送到你的邮箱,请查收。 11 |

12 | 13 | {% endblock %} 14 | -------------------------------------------------------------------------------- /scripts/invalidate_problem_stats.py: -------------------------------------------------------------------------------- 1 | import progressbar 2 | 3 | from problem.models import Problem 4 | from problem.statistics import invalidate_problem 5 | 6 | 7 | def run(*args): 8 | for problem in progressbar.progressbar(Problem.objects.all()): 9 | invalidate_problem(problem) 10 | -------------------------------------------------------------------------------- /templates/contest/activity/add.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block title %}Activities - {% endblock %} 4 | 5 | {% block page_header %}Add an activity{% endblock %} 6 | 7 | {% block content %} 8 | 9 | {% include 'components/form.jinja2' %} 10 | 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /templates/contest/activity/admin_add.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block title %}Activities - {% endblock %} 4 | 5 | {% block page_header %}Add a user{% endblock %} 6 | 7 | {% block content %} 8 | 9 | {% include 'components/form.jinja2' %} 10 | 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /static/less/simplemde.less: -------------------------------------------------------------------------------- 1 | .CodeMirror-fullscreen { 2 | z-index: 109 !important; 3 | } 4 | 5 | .editor-toolbar.fullscreen { 6 | z-index: 109 !important; 7 | } 8 | 9 | .CodeMirror:not(.CodeMirror-fullscreen) { 10 | min-height: 200px !important; 11 | height: 300px !important; 12 | } 13 | -------------------------------------------------------------------------------- /templates/contest/activity/admin_update.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block title %}Activities - {% endblock %} 4 | 5 | {% block page_header %}Update a user{% endblock %} 6 | 7 | {% block content %} 8 | 9 | {% include 'components/form.jinja2' %} 10 | 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /templates/contest/activity/edit.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block title %}Activities - {% endblock %} 4 | 5 | {% block page_header %}Update an activity{% endblock %} 6 | 7 | {% block content %} 8 | 9 | {% include 'components/form.jinja2' %} 10 | 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /templates/contest/activity/school_form.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block title %}Add a school - {% endblock %} 4 | 5 | {% block page_header %}Add a school{% endblock %} 6 | 7 | {% block content %} 8 | 9 | {% include 'components/form.jinja2' %} 10 | 11 | {% endblock %} 12 | -------------------------------------------------------------------------------- /utils/serve_nginx.py: -------------------------------------------------------------------------------- 1 | from django.http import HttpResponse 2 | from django.utils.http import urlquote 3 | 4 | def serve_with_nginx(request, path, root_name=None): 5 | response = HttpResponse() 6 | response['X-Accel-Redirect'] = "/fake/{0}/{1}".format(root_name, urlquote(path)) 7 | return response 8 | -------------------------------------------------------------------------------- /problem/models/tag.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | from tagging.models import Tag 4 | 5 | 6 | class TagInfo(models.Model): 7 | tag = models.OneToOneField(Tag, on_delete=models.CASCADE) 8 | description = models.TextField(blank=True) 9 | parent_id = models.IntegerField(default=-1) 10 | -------------------------------------------------------------------------------- /templates/captcha/image.html: -------------------------------------------------------------------------------- 1 | {% load i18n %} 2 | {% spaceless %} 3 | {% if audio %}{% endif %} 4 | captcha{% if audio %}{% endif %} 5 | {% endspaceless %} -------------------------------------------------------------------------------- /templates/components/timeanddate_link.jinja2: -------------------------------------------------------------------------------- 1 | {% macro timeanddate_link(time) %} 2 | {{ time | date('Y-m-d H:i') }} 3 | {% endmacro %} -------------------------------------------------------------------------------- /utils/tagging.py: -------------------------------------------------------------------------------- 1 | def edit_string_for_tags(tags): 2 | """ 3 | Simplified version of comma separated tags 4 | """ 5 | names = [] 6 | for tag in tags: 7 | name = tag.name 8 | if ',' in name: 9 | names.append('"%s"' % name) 10 | continue 11 | names.append(name) 12 | return ','.join(names) 13 | -------------------------------------------------------------------------------- /templates/account/password_reset_confirm.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block title %}密码确认 - {% endblock %} 4 | 5 | {% block page_header %}{{ title }}{% endblock %} 6 | 7 | {% block content %} 8 | 9 | {% if form %} 10 | {% include 'components/form.jinja2' %} 11 | {% endif %} 12 | 13 | {% endblock %} 14 | -------------------------------------------------------------------------------- /utils/detail_formatter.py: -------------------------------------------------------------------------------- 1 | import traceback 2 | from datetime import datetime 3 | 4 | 5 | def add_timestamp_to_reply(data): 6 | data.update(timestamp=datetime.now().timestamp()) 7 | return data 8 | 9 | 10 | def response_fail_with_timestamp(): 11 | return add_timestamp_to_reply({'status': 'reject', 'message': traceback.format_exc()}) 12 | -------------------------------------------------------------------------------- /problem/models/feedback.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | from account.models import User 4 | 5 | 6 | class FeedbackCompare(models.Model): 7 | user = models.ForeignKey(User, null=True, on_delete=models.SET_NULL) 8 | problem_1 = models.PositiveIntegerField() 9 | problem_2 = models.PositiveIntegerField() 10 | create_time = models.DateTimeField(auto_now_add=True) 11 | -------------------------------------------------------------------------------- /problem/testlib/checker/pointscmp.cpp: -------------------------------------------------------------------------------- 1 | #include "testlib.h" 2 | 3 | using namespace std; 4 | 5 | int main(int argc, char * argv[]) 6 | { 7 | setName("example of scored checker"); 8 | registerTestlibCmd(argc, argv); 9 | 10 | double ja = ans.readDouble(); 11 | double pa = ouf.readDouble(); 12 | 13 | quitp(fabs(ja - pa), "ja=%.4f pa=%.4f", ja, pa); 14 | } 15 | -------------------------------------------------------------------------------- /templates/taggraph.jinja2: -------------------------------------------------------------------------------- 1 | 2 | 3 | 21 | -------------------------------------------------------------------------------- /templates/backstage/problem/source_edit.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'backstage/base.jinja2' %} 2 | 3 | {% block content_header %} 4 | Edit Source 5 | {% endblock %} 6 | 7 | {% block backstage_content %} 8 | 9 |
10 | This will update the source of problems in the interval. 11 |
12 | 13 | {% include 'components/form.jinja2' %} 14 | {% endblock %} 15 | -------------------------------------------------------------------------------- /templates/components/post_link.jinja2: -------------------------------------------------------------------------------- 1 | {% macro post_link(selector) %} 2 | 11 | {% endmacro %} -------------------------------------------------------------------------------- /paste/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | 3 | from .views import PasteListAndCreateView, PasteDetailView, PasteDeleteView 4 | 5 | app_name = "paste" 6 | 7 | urlpatterns = [ 8 | url(r'^$', PasteListAndCreateView.as_view(), name='index'), 9 | url(r'^(?P\w+)/$', PasteDetailView.as_view(), name='detail'), 10 | url(r'^(?P\w+)/delete/$', PasteDeleteView.as_view(), name='delete'), 11 | ] 12 | -------------------------------------------------------------------------------- /templates/register.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block page_header %}注册{% endblock %} 4 | 5 | {% block content %} 6 | 7 |
8 | {% include 'components/form.jinja2' %} 9 |
10 |

已有账户?

11 | 12 | {% endblock %} 13 | 14 | {% block script %} 15 | {% include 'components/rsa_encrypt.jinja2' %} 16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /templates/error/closed.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block page_header %}EOJ is closed now{% endblock %} 4 | 5 | {% block content %} 6 | 7 |
8 |

EOJ is currently closed due to maintenance or running contests.

9 |

Please try again later or see contests running here.

10 |
11 | 12 | {% endblock %} 13 | -------------------------------------------------------------------------------- /templates/login.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block page_header %}登录{% endblock %} 4 | 5 | {% block content %} 6 | 7 | {% include 'components/form.jinja2' %} 8 |

忘记密码 | 注册账号

9 | 10 | {% endblock %} 11 | 12 | {% block script %} 13 | {% include 'components/rsa_encrypt.jinja2' %} 14 | {% endblock %} 15 | -------------------------------------------------------------------------------- /notification/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | 3 | import notification.views as v 4 | 5 | app_name = 'notification' 6 | 7 | urlpatterns = [ 8 | url(r'^$', v.NotificationListView.as_view(), name='list'), 9 | url(r'^api/notified/(?P\d+)/$', v.NotificationMarkAsRead.as_view(), name='notified'), 10 | url(r'^api/mark_all_as_read/$', v.NotificationMarkAllAsRead.as_view(), name='mark_all_as_read'), 11 | ] 12 | -------------------------------------------------------------------------------- /utils/debug.py: -------------------------------------------------------------------------------- 1 | from debug_toolbar.panels.templates import TemplatesPanel as BaseTemplatesPanel 2 | 3 | 4 | class TemplatesPanel(BaseTemplatesPanel): 5 | def generate_stats(self, *args): # pylint: disable=arguments-differ 6 | template = self.templates[0]['template'] 7 | if not hasattr(template, 'engine') and hasattr(template, 'backend'): 8 | template.engine = template.backend 9 | return super().generate_stats(*args) 10 | -------------------------------------------------------------------------------- /backstage/server/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | 3 | from dispatcher.models import Server 4 | 5 | 6 | class ServerEditForm(forms.ModelForm): 7 | class Meta: 8 | model = Server 9 | fields = ['name', 'ip', 'port', 'token', 'concurrency', 'runtime_multiplier', 'version', 'master'] 10 | 11 | 12 | class ServerUpdateTokenForm(forms.Form): 13 | new_password = forms.CharField(min_length=4, max_length=128, label='New Password') 14 | -------------------------------------------------------------------------------- /eoj3/captcha.py: -------------------------------------------------------------------------------- 1 | from random import randint, choice 2 | 3 | SIGN = { 4 | '+': 'plus', 5 | '-': 'minus', 6 | '*': 'times', 7 | } 8 | 9 | 10 | def random_math_challenge(): 11 | a, b = randint(1, 10), randint(1, 9) 12 | c = choice('+-*') 13 | if c == '+': 14 | d = a + b 15 | elif c == '-': 16 | d = a - b 17 | elif c == '*': 18 | d = a * b 19 | else: 20 | d = 0 21 | return '%d %s %d' % (a, SIGN[c], b), str(d) 22 | -------------------------------------------------------------------------------- /problem/testlib/checker/icmp.cpp: -------------------------------------------------------------------------------- 1 | #include "testlib.h" 2 | #include 3 | 4 | int main(int argc, char * argv[]) 5 | { 6 | setName("compare two signed int%ld's", 8 * sizeof(int)); 7 | registerTestlibCmd(argc, argv); 8 | 9 | int ja = ans.readInt(); 10 | int pa = ouf.readInt(); 11 | 12 | if (ja != pa) 13 | quitf(_wa, "expected %d, found %d", ja, pa); 14 | 15 | quitf(_ok, "answer is %d", ja); 16 | } 17 | -------------------------------------------------------------------------------- /problem/testlib/validator/ival.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Validates that input contains the only integer between 1 and 100, inclusive. 3 | * Also validates that file ends with EOLN and EOF. 4 | */ 5 | 6 | #include "testlib.h" 7 | 8 | using namespace std; 9 | 10 | int main(int argc, char* argv[]) 11 | { 12 | registerValidation(argc, argv); 13 | 14 | inf.readInt(1, 100, "n"); 15 | inf.readEoln(); 16 | inf.readEof(); 17 | 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /templates/problem/detail/discussion.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'problem/detail/base.jinja2' %} 2 | 3 | 4 | {% block problem_content %} 5 | 6 | {% set submit_button %} 7 | 10 | {% endset %} 11 | 12 | {{ render_comment_tree(problem) }} 13 | {% include 'comments/comment_reply.jinja2' %} 14 | 15 | {% endblock %} 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /problem/models/skill.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class Skill(models.Model): 5 | name = models.CharField(max_length=64) 6 | description = models.TextField(blank=True) 7 | parent_id = models.IntegerField(default=-1) 8 | problem_list = models.TextField(blank=True) 9 | priority = models.IntegerField(default=0) 10 | 11 | @property 12 | def parsed_problem_list(self): 13 | return list(map(int, filter(lambda x: x, self.problem_list.split(',')))) 14 | -------------------------------------------------------------------------------- /problem/migrations/0002_auto_20201222_1327.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.17 on 2020-12-22 13:27 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('problem', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='problem', 15 | name='update_time', 16 | field=models.DateTimeField(), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /api/views/user.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from rest_framework.generics import RetrieveAPIView 3 | 4 | from account.models import User 5 | 6 | 7 | class UserSerializer(serializers.ModelSerializer): 8 | class Meta: 9 | model = User 10 | fields = ( 11 | "username", "school", "name", 12 | "student_id", "avatar", "score", 13 | ) 14 | 15 | 16 | class UserView(RetrieveAPIView): 17 | queryset = User.objects.all() 18 | serializer_class = UserSerializer 19 | -------------------------------------------------------------------------------- /templates/report_download.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block title %}下载报告 - {% endblock %} 4 | 5 | {% block page_header %}下载报告{% endblock %} 6 | 7 | {% block content %} 8 |

请确认支付 {{ price | round(1) }} EMB 以获取提交 #{{ submission }}的评测报告。

9 |
10 | {% csrf_token %} 11 | 12 | 13 |
14 | 15 | {% endblock %} -------------------------------------------------------------------------------- /utils/url_formatter.py: -------------------------------------------------------------------------------- 1 | def url_linker(host, port, path): 2 | if not host.startswith('http'): 3 | if host.startswith('//'): 4 | host = 'http:' + host 5 | else: 6 | host = 'http://' + host 7 | if not path.startswith('/'): 8 | path = '/' + path 9 | return host + ':' + str(port) + path 10 | 11 | 12 | def upload_linker(host, port, pid): 13 | return url_linker(host, port, 'upload/%s' % str(pid)) 14 | 15 | 16 | def judge_linker(host, port): 17 | return url_linker(host, port, 'judge') 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # junk files 2 | [Tt]humbs.db 3 | *.DS_Store 4 | .idea/ 5 | __pycache__/ 6 | .vscode 7 | 8 | # Database 9 | *.sqlite3 10 | *.sqlite 11 | 12 | # ignore datadir 13 | data/ 14 | upload/ 15 | testdata/ 16 | cases/ 17 | generate/ 18 | media/ 19 | repo/ 20 | logs/ 21 | 22 | # local settings 23 | local_settings.py 24 | 25 | # dev env 26 | init.bat 27 | 28 | # library 29 | cdn/ 30 | node_modules/ 31 | bower_components/ 32 | venv/ 33 | package-lock.json 34 | 35 | # generated css 36 | app.css 37 | app.min.css 38 | -------------------------------------------------------------------------------- /contest/migrations/0002_contestinvitation_availability.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.10 on 2021-12-15 17:01 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('contest', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='contestinvitation', 15 | name='availability', 16 | field=models.IntegerField(default=1), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /migrate/forms.py: -------------------------------------------------------------------------------- 1 | from captcha.fields import CaptchaField 2 | from django import forms 3 | 4 | 5 | class MigrateForm(forms.Form): 6 | is_new = forms.ChoiceField(label='注册时间', choices=( 7 | ('old', '在 2017 年二月之前在旧 EOJ 中注册'), 8 | ('new', '在 2017 年五月之后在新 EOJ 中注册') 9 | )) 10 | username = forms.CharField(label='你想要注销掉的用户名', max_length=224, required=True) 11 | password = forms.CharField(label='那个账户的密码', widget=forms.PasswordInput, max_length=224, required=True) 12 | captcha = CaptchaField(label="小学数学题") 13 | -------------------------------------------------------------------------------- /templates/components/username_display.jinja2: -------------------------------------------------------------------------------- 1 | {{ username }} {% if ghost %}{% endif %}{% if in_contest %}{% elif legend %}{% endif %} 2 | -------------------------------------------------------------------------------- /api/views/tag.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | from django.http import HttpResponse 4 | from tagging.models import TaggedItem 5 | 6 | from problem.models import Problem 7 | 8 | 9 | def return_problems(request): 10 | if request.method == "GET": 11 | tag = request.GET.get("tag", default="tree") 12 | queryset = TaggedItem.objects.get_by_model(Problem, tag) 13 | problem_id_dic = {"problems": list(queryset.values_list("id", flat=True))} 14 | return HttpResponse(json.dumps(problem_id_dic), content_type="application/json") 15 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # junk files 2 | [Tt]humbs.db 3 | *.DS_Store 4 | .idea/ 5 | __pycache__/ 6 | .vscode 7 | 8 | # Database 9 | *.sqlite3 10 | *.sqlite 11 | 12 | # ignore datadir 13 | data/ 14 | upload/ 15 | testdata/ 16 | cases/ 17 | generate/ 18 | media/ 19 | repo/ 20 | logs/ 21 | 22 | # local settings 23 | local_settings.py 24 | 25 | # dev env 26 | init.bat 27 | 28 | # library 29 | cdn/ 30 | node_modules/ 31 | bower_components/ 32 | venv/ 33 | package-lock.json 34 | 35 | # generated css 36 | app.css 37 | app.min.css 38 | 39 | Dockerfile -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ECNU Online Judge 2 | 3 | ## 常见问题 4 | 5 | 没有文档。 6 | 7 | 对华东师范大学本科 / 硕博感兴趣的:https://acm.ecnu.edu.cn/ 8 | 9 | 受 policy 影响时不时会上不去,可以尝试备用域名:https://eoj.i64d.com/ 10 | 11 | 如果只是为了做题,有很多更好的 OJ 可以用。 12 | 13 | 想要部署本 OJ 的,建议不要做这种没有前途的浪费人生的事情。世界很大,有很多更好的 OJ 可以用。 14 | 15 | **For international readers:** We don't provide documentation/software for international users any more. 16 | 17 | ## 重构计划 18 | 19 | 因为野鸡群主要毕业,重构可能要鸽鸽了。 20 | 21 | ## 联系我们 22 | 23 | 如果以上说明没能解决你的困惑,可以加入野鸡群主自建群 691713742。入群问题答案不带空格。群里大多数时间都没人说话,说话也跟 OJ 没啥关系。 24 | -------------------------------------------------------------------------------- /contest/migrations/0003_auto_20211215_1726.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.10 on 2021-12-15 17:26 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('contest', '0002_contestinvitation_availability'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='contestinvitation', 15 | name='availability', 16 | field=models.PositiveIntegerField(default=1), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /problem/testlib/checker/acmp.cpp: -------------------------------------------------------------------------------- 1 | #include "testlib.h" 2 | #include 3 | #include 4 | 5 | const double EPS = 1.5E-6; 6 | 7 | int main(int argc, char * argv[]) 8 | { 9 | setName("compare two doubles, maximal absolute error = %.10f", EPS); 10 | registerTestlibCmd(argc, argv); 11 | 12 | double ja = ans.readDouble(); 13 | double pa = ouf.readDouble(); 14 | 15 | if (fabs(ja - pa) > EPS + 1E-15) 16 | quitf(_wa, "expected %.10f, found %.10f", ja, pa); 17 | 18 | quitf(_ok, "answer is %.10f", ja); 19 | } 20 | -------------------------------------------------------------------------------- /problem/testlib/checker/rcmp.cpp: -------------------------------------------------------------------------------- 1 | #include "testlib.h" 2 | #include 3 | #include 4 | 5 | const double EPS = 1.5E-6; 6 | 7 | int main(int argc, char * argv[]) 8 | { 9 | setName("compare two doubles, maximal absolute error = %.10f", EPS); 10 | registerTestlibCmd(argc, argv); 11 | 12 | double ja = ans.readDouble(); 13 | double pa = ouf.readDouble(); 14 | 15 | if (fabs(ja - pa) > EPS + 1E-15) 16 | quitf(_wa, "expected %.10f, found %.10f", ja, pa); 17 | 18 | quitf(_ok, "answer is %.10f", ja); 19 | } 20 | -------------------------------------------------------------------------------- /tests/views.py: -------------------------------------------------------------------------------- 1 | from random import randint 2 | 3 | from django.http import JsonResponse, HttpResponse 4 | from django.views.decorators.csrf import csrf_exempt 5 | 6 | 7 | def response_ok(): 8 | return {"status": "received"} 9 | 10 | 11 | @csrf_exempt 12 | def judge_mock(request): 13 | return JsonResponse(response_ok()) 14 | 15 | 16 | @csrf_exempt 17 | def query_mock(request): 18 | return JsonResponse({"status": "received", "verdict": randint(-2, 0)}) 19 | 20 | 21 | @csrf_exempt 22 | def query_report_mock(request): 23 | return HttpResponse() 24 | -------------------------------------------------------------------------------- /problem/testlib/validator/sval.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Validates that the input contains the only token token. 3 | * This token can contain only lowercase latin letters a-z. The length should be between 1 and 100, inclusive. 4 | * Also validates that file ends with EOLN and EOF. 5 | */ 6 | 7 | #include "testlib.h" 8 | 9 | using namespace std; 10 | 11 | int main(int argc, char* argv[]) 12 | { 13 | registerValidation(argc, argv); 14 | 15 | inf.readToken("[a-z]{1,100}", "s"); 16 | inf.readEoln(); 17 | inf.readEof(); 18 | 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /templates/account/password_reset_email.jinja2: -------------------------------------------------------------------------------- 1 | {% autoescape off %} 2 | You're receiving this email because you requested a password reset for your user account at {{ site_name }}. 3 | 4 | Please go to the following page and choose a new password: 5 | {% block reset_link %} 6 | {{ protocol }}://{{ domain }}{{ url('account:password_reset_confirm', uidb64=uid, token=token) }} 7 | {% endblock %} 8 | Your username, in case you've forgotten: {{ user.get_username() }} 9 | 10 | Thanks for using our site! 11 | 12 | The {{ site_name }} team 13 | 14 | {% endautoescape %} 15 | -------------------------------------------------------------------------------- /problem/testlib/checker/dcmp.cpp: -------------------------------------------------------------------------------- 1 | #include "testlib.h" 2 | #include 3 | #include 4 | 5 | const double EPS = 1E-6; 6 | 7 | int main(int argc, char * argv[]) 8 | { 9 | setName("compare two doubles, maximal absolute or relative error = %.10f", EPS); 10 | registerTestlibCmd(argc, argv); 11 | 12 | double ja = ans.readDouble(); 13 | double pa = ouf.readDouble(); 14 | 15 | if (!doubleCompare(ja, pa, EPS)) 16 | quitf(_wa, "expected %.10f, found %.10f", ja, pa); 17 | 18 | quitf(_ok, "answer is %.10f", ja); 19 | } 20 | -------------------------------------------------------------------------------- /templates/components/delete_confirmation.jinja2: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /polygon/templates/polygon/problem2/template/preview.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'polygon/problem2/base.jinja2' %} 2 | 3 | {% block problem_content %} 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
Language{{ template.get_language_display() }}
Template{{ template.template_code_as_html | safe }}
Grader{{ template.grader_code_as_html | safe }}
20 | {% endblock %} -------------------------------------------------------------------------------- /utils/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.9 on 2020-02-04 02:11 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='SiteSettings', 16 | fields=[ 17 | ('key', models.CharField(max_length=254, primary_key=True, serialize=False)), 18 | ('val', models.TextField(blank=True)), 19 | ], 20 | ), 21 | ] 22 | -------------------------------------------------------------------------------- /static/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "foundation-sites-template", 3 | "version": "1.0.0", 4 | "authors": [ 5 | "ZURB " 6 | ], 7 | "description": "Basic template for a new Foundation for Sites project.", 8 | "main": "index.html", 9 | "license": "MIT", 10 | "homepage": "http://foundation.zurb.com", 11 | "dependencies": { 12 | "foundation-sites": "~6.4.0", 13 | "motion-ui": "~1.2.3" 14 | }, 15 | "ignore": [ 16 | "**/.*", 17 | "node_modules", 18 | "bower_components", 19 | "test", 20 | "tests" 21 | ], 22 | "private": true 23 | } 24 | -------------------------------------------------------------------------------- /templates/contest/standings_penalty_detail.jinja2: -------------------------------------------------------------------------------- 1 |
2 | {% for submission in submission_list %} 3 |
{{ submission.create_time | date('Y-m-d H:i:s') }}
{{ submission.id }}{% if submission.lang %}, {{ submission.get_lang_display() }}{% endif %}
4 | {% endfor %} 5 |
-------------------------------------------------------------------------------- /problem/testlib/generator/bgen.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Outputs random 100-digits binary string mostly containing 0's. 3 | * In average it contrains only 10% of 1's. 4 | * 5 | * To generate different values, call "bgen.exe" with different parameters. 6 | * 7 | * It is typical behaviour of testlib generator to setup randseed by command line. 8 | */ 9 | 10 | #include "testlib.h" 11 | #include 12 | 13 | using namespace std; 14 | 15 | int main(int argc, char* argv[]) 16 | { 17 | registerGen(argc, argv, 1); 18 | 19 | cout << rnd.next("[0000000001]{100}") << endl; 20 | 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /problem/testlib/generator/igen.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Outputs random number between 1 and 10^6, inclusive. 3 | * To generate different values, call "igen.exe" with different parameters. 4 | * For example, "igen.exe 1" returns 504077, but "igen.exe 3" returns 808747. 5 | * 6 | * It is typical behaviour of testlib generator to setup randseed by command line. 7 | */ 8 | 9 | #include "testlib.h" 10 | #include 11 | 12 | using namespace std; 13 | 14 | int main(int argc, char* argv[]) 15 | { 16 | registerGen(argc, argv, 1); 17 | 18 | cout << rnd.next(1, 1000000) << endl; 19 | 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /templates/components/search_user.jinja2: -------------------------------------------------------------------------------- 1 | {% macro user_search_multiple(name="", extra_class="", query="", exist=[]) %} 2 | 12 | {% endmacro %} -------------------------------------------------------------------------------- /templates/error/404.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'html_base.jinja2' %} 2 | 3 | {% block css %} 4 | 15 | {% endblock %} 16 | 17 | {% block _content %} 18 |
19 | 20 |
21 | 22 |

23 | Oops! Page Not Found... 24 |

25 |
26 |
27 | {% endblock %} 28 | -------------------------------------------------------------------------------- /templates/account/migrate.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'account/base.jinja2' %} 2 | {% block account_setting %} 3 | 4 |
5 |
请仔细阅读:
6 |
    7 |
  • 如果是原 EOJ 中 (acm.cs.ecnu.edu.cn) 中的账号,可以通过输入用户名和密码来迁移,其中 2017 年二月之后的数据将无法迁移。
  • 8 |
  • 如果是新 EOJ 中的账号(包括比赛中分发的账号),可以通过输入用户名和密码来迁移。
  • 9 |
  • 比赛中的提交将会被再次提交,而题目集中的提交会进行转移所有者。
  • 10 |
  • 被迁移的用户会在迁移结束后被禁用,且博客和评论不会被迁移,请再三确认以防数据丢失。
  • 11 |
12 |
13 | 14 | {% include 'components/form.jinja2' %} 15 | {% endblock %} -------------------------------------------------------------------------------- /utils/csv_writer.py: -------------------------------------------------------------------------------- 1 | import csv 2 | from os import path 3 | 4 | from django.conf import settings 5 | 6 | from . import random_string 7 | 8 | 9 | def write_csv(data): 10 | """ 11 | :param data: 12 | :return: csv file name 13 | """ 14 | file_name = random_string() 15 | file_path = path.join(settings.GENERATE_DIR, file_name) 16 | with open(file_path, 'w', newline='', encoding='utf-8-sig') as csvfile: 17 | # csv files need to be encoded using UTF-8 BOM to be correctly opened in Excel on Windows 18 | writer = csv.writer(csvfile) 19 | for d in data: 20 | writer.writerow(d) 21 | return file_path 22 | -------------------------------------------------------------------------------- /problem/testlib/generator/sgen.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Outputs random token. 3 | * 4 | * Token contains latin letters and digits and have length 5 | * between 1 and 1000 characters, inclusive. 6 | * 7 | * To generate different values, call "sgen.exe" with different parameters. 8 | * 9 | * It is typical behaviour of testlib generator to setup randseed by command line. 10 | */ 11 | 12 | #include "testlib.h" 13 | #include 14 | 15 | using namespace std; 16 | 17 | int main(int argc, char* argv[]) 18 | { 19 | registerGen(argc, argv, 1); 20 | 21 | cout << rnd.next("[a-zA-Z0-9]{1,1000}") << endl; 22 | 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /static/js/markdown.js: -------------------------------------------------------------------------------- 1 | $.fn.simpleMDE = function () { 2 | $(this).each(function () { 3 | new SimpleMDE({ 4 | element: this, 5 | forceSync: true, 6 | previewRender: _.debounce(function (plainText, preview) { 7 | $.post("/api/markdown/", { 8 | csrfmiddlewaretoken: Cookies.get('csrftoken'), 9 | text: plainText || "" 10 | }, function (data) { 11 | preview.innerHTML = data; 12 | MathJax.typeset(); 13 | }.bind(this)); 14 | return "Loading..."; 15 | }, 1000), 16 | spellChecker: false 17 | }); 18 | }); 19 | }; 20 | 21 | $("textarea.markdown").simpleMDE(); 22 | -------------------------------------------------------------------------------- /api/examples.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | import requests 4 | 5 | 6 | class APITestMethods(unittest.TestCase): 7 | def setUp(self): 8 | self.host = "http://127.0.0.1:8000" 9 | data = requests.post(self.host + "/api/token/", json={"username": "ultmaster", "password": "LkEMLc0nc8Hf"}).json() 10 | self.authorization = {"Authorization": "Bearer " + data["access"]} 11 | 12 | def test_status_hidden(self): 13 | data = requests.get(self.host + "/api/status/hidden/?limit=10&offset=10", headers=self.authorization).json() 14 | print(data) 15 | 16 | def test_problem(self): 17 | pass 18 | 19 | if __name__ == '__main__': 20 | unittest.main() 21 | -------------------------------------------------------------------------------- /templates/problem/source.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'problem/base.jinja2' %} 2 | 3 | {% block problem_content %} 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | {% for source in source_list %} 14 | 15 | 18 | 19 | 20 | {% endfor %} 21 | 22 |
名称#
16 | {{ source.source }} 17 | {{ source.count }}
23 | 24 | {% endblock %} 25 | -------------------------------------------------------------------------------- /utils/multiple_choice_field.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | 3 | 4 | class CommaSeparatedMultipleChoiceField(forms.MultipleChoiceField): 5 | 6 | def _split_comma(self, value): 7 | return list(filter(lambda u: u, map(lambda t: t.strip(), value.split(',')))) 8 | 9 | def to_python(self, value): 10 | if ',' in value: 11 | return self._split_comma(value) 12 | if isinstance(value, (tuple, list)): 13 | return self._split_comma(value[0]) 14 | return super(CommaSeparatedMultipleChoiceField, self).to_python(value) 15 | 16 | 17 | class UserSelectMultiple(CommaSeparatedMultipleChoiceField): 18 | 19 | def validate(self, value): 20 | pass 21 | -------------------------------------------------------------------------------- /polygon/templates/polygon/problem2/package.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'polygon/problem2/base.jinja2' %} 2 | 3 | {% block problem_content %} 4 |
5 |
你正在执行导入操作!
6 |
    7 |
  • 支持从 Codeforces Polygon 导出的数据包导入。请下载 Linux 平台的 Full Package,并直接上传下载到的 zip 文件。
  • 8 |
  • 数据导入时会自动将 \r\n 转换成平台支持的 \n。
  • 9 |
  • 题面格式 (TeX) 不会自动转换成 Markdown,需要手动调整。
  • 10 |
  • 附件需要手动上传并调整引用。
  • 11 |
  • 导入操作会覆盖该版本原有的数据。请谨慎操作!
  • 12 |
13 |
14 | 15 | {% include 'components/form.jinja2' %} 16 | {% endblock %} 17 | -------------------------------------------------------------------------------- /templates/problem/reward.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'problem/base.jinja2' %} 2 | {% from 'components/blog_preview.jinja2' import blog_preview %} 3 | {% block problem_content %} 4 |
5 | 6 |
7 | 8 | {% if is_author %}写博客{% endif %} 9 |
10 | {% if reward_list %} 11 | {% for reward in reward_list %} 12 | {{ blog_preview(reward) }} 13 | {% endfor %} 14 | {% endif %} 15 |
16 |
17 |
18 | 19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /templates/error/500.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'html_base.jinja2' %} 2 | 3 | {% block css %} 4 | 15 | {% endblock %} 16 | 17 | {% block _content %} 18 |
19 | 20 |
21 | 22 |

23 | Be back soon... 24 |
An email has been sent to staff.
25 |

26 |
27 |
28 | {% endblock %} 29 | -------------------------------------------------------------------------------- /account/migrations/0002_auto_20210411_1442.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.10 on 2021-04-11 14:42 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('account', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='user', 15 | name='magic', 16 | field=models.CharField(blank=True, choices=[('legendary', 'Legendary'), ('red', 'Red'), ('green', 'Green'), ('teal', 'Teal'), ('blue', 'Blue'), ('purple', 'Purple'), ('orange', 'Orange'), ('grey', 'Grey')], max_length=18, verbose_name='魔法'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /polygon/templates/polygon/contest/status.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'polygon/contest/base.jinja2' %} 2 | {% from 'components/status.jinja2' import status with context %} 3 | 4 | {% block contest_content %} 5 | 6 | {{ status(rejudge=True, show_ip=True) }} 7 | 8 |

导入提交记录

9 |

导入 TestSys 格式的比赛记录

10 | 11 | {% if contest.run_tests_during_contest != "all" %} 12 |

当心!

13 |

开始系统测试(重测所有提交)

14 | {% endif %} 15 | 16 | {% endblock %} -------------------------------------------------------------------------------- /templates/contest/activity/confirm_complete.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'html_base.jinja2' %} 2 | 3 | {% block css %} 4 | 15 | {% endblock %} 16 | 17 | {% block _content %} 18 |
19 | 20 |
21 | 22 |

23 | Comfirm Complete 24 |
Thank you for your time.
25 |

26 |
27 |
28 | {% endblock %} 29 | -------------------------------------------------------------------------------- /api/views/problemlist.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from rest_framework.generics import RetrieveAPIView 3 | 4 | from problem.models import Problem 5 | 6 | 7 | class ProblemListSerializer(serializers.ModelSerializer): 8 | class Meta: 9 | model = Problem 10 | fields = ["problem_list_info"] 11 | 12 | problem_list_info = serializers.SerializerMethodField() 13 | 14 | def get_problem_list_info(self, problem): 15 | problems = Problem.objects.filter(visible=True).order_by("id") 16 | return [str(prob) for prob in problems] 17 | 18 | 19 | class ProblemListView(RetrieveAPIView): 20 | queryset = Problem.objects.filter(visible=True) 21 | serializer_class = ProblemListSerializer 22 | -------------------------------------------------------------------------------- /problem/testlib/checker/rncmp.cpp: -------------------------------------------------------------------------------- 1 | #include "testlib.h" 2 | #include 3 | 4 | using namespace std; 5 | 6 | const double EPS = 1.5E-5; 7 | 8 | int main(int argc, char * argv[]) 9 | { 10 | setName("compare two sequences of doubles, maximal absolute error = %.10f", EPS); 11 | registerTestlibCmd(argc, argv); 12 | 13 | int n = 0; 14 | while (!ans.seekEof()) 15 | { 16 | n++; 17 | double j = ans.readDouble(); 18 | double p = ouf.readDouble(); 19 | if (fabs(j - p) > EPS + 1E-15) 20 | quitf(_wa, "%d%s numbers differ - expected: '%.10f', found: '%.10f'", n, englishEnding(n).c_str(), j, p); 21 | } 22 | 23 | quitf(_ok, "%d numbers", n); 24 | } 25 | -------------------------------------------------------------------------------- /.github/workflows/docker-image.yml: -------------------------------------------------------------------------------- 1 | name: Docker Image CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - uses: azure/docker-login@v1 13 | with: 14 | login-server: registry.cn-hangzhou.aliyuncs.com 15 | username: ${{ secrets.DOCKER_USERNAME }} 16 | password: ${{ secrets.DOCKER_PASSWORD }} 17 | - uses: actions/checkout@v2 18 | - name: Build the Docker image 19 | run: docker build . --file Dockerfile --tag registry.cn-hangzhou.aliyuncs.com/foreigners/eoj3:latest 20 | - name: Upload docker image 21 | run: docker push registry.cn-hangzhou.aliyuncs.com/foreigners/eoj3:latest 22 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:8 as yarn 2 | 3 | RUN mkdir /static 4 | WORKDIR /static 5 | COPY ./static/.bowerrc ./static/bower.json ./static/package.json ./static/yarn.lock ./ 6 | RUN yarn install --production=true --frozen-lockfile 7 | 8 | FROM yarn as gulp 9 | 10 | RUN yarn install --frozen-lockfile 11 | COPY ./static/ ./ 12 | RUN yarn build 13 | 14 | FROM python:3.7 as base 15 | 16 | RUN apt update && apt install -y openjdk-11-jre-headless && apt clean 17 | 18 | WORKDIR /eoj3 19 | COPY ./requirements.txt ./ 20 | RUN pip3 install --no-cache-dir -r requirements.txt 21 | COPY --from=yarn /static/ ./static 22 | COPY --from=gulp /static/css/ ./static/css 23 | COPY . ./ 24 | 25 | EXPOSE 80 26 | EXPOSE 3031 27 | 28 | WORKDIR /eoj3 -------------------------------------------------------------------------------- /eoj3/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for eoj3 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.10/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | from os.path import dirname, abspath 12 | 13 | from django.core.wsgi import get_wsgi_application 14 | 15 | PROJECT_DIR = dirname(dirname(abspath(__file__))) 16 | import sys # pylint: disable=wrong-import-position,wrong-import-order 17 | 18 | sys.path.insert(0, PROJECT_DIR) 19 | sys.path.insert(0, dirname(PROJECT_DIR)) 20 | 21 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "eoj3.settings") 22 | 23 | application = get_wsgi_application() 24 | -------------------------------------------------------------------------------- /problem/models/status.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | from account.models import User 4 | 5 | 6 | class UserStatus(models.Model): 7 | user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="submission_status") 8 | contest_id = models.PositiveIntegerField(db_index=True) 9 | total_count = models.PositiveIntegerField() 10 | total_list = models.TextField(blank=True) 11 | ac_count = models.PositiveIntegerField() 12 | ac_distinct_count = models.PositiveIntegerField() 13 | ac_list = models.TextField(blank=True) 14 | predict_list = models.TextField(blank=True) 15 | update_time = models.DateTimeField(auto_now=True) 16 | 17 | class Meta: 18 | unique_together = ('user', 'contest_id') 19 | -------------------------------------------------------------------------------- /templates/contest/reward.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'contest/base.jinja2' %} 2 | {% from 'components/status.jinja2' import status with context %} 3 | {% from 'components/blog_preview.jinja2' import blog_preview %} 4 | 5 | {% block contest_content %} 6 | 7 |
8 | 9 |
10 | 11 | {% if is_author %}写博客{% endif %} 12 |
13 | {% if blog_list %} 14 | {% for blog in blog_list %} 15 | {{ blog_preview(blog) }} 16 | {% endfor %} 17 | {% endif %} 18 |
19 |
20 | 21 |
22 | 23 | {% endblock %} -------------------------------------------------------------------------------- /utils/email.py: -------------------------------------------------------------------------------- 1 | import time 2 | import traceback 3 | 4 | from django.core.mail import EmailMultiAlternatives, get_connection 5 | 6 | 7 | def send_mail_with_bcc(subject, html_message, recipient_list, fail_silently=False): 8 | def divide_group(lst, k): 9 | return [lst[i:i + k] for i in range(0, len(lst), k)] 10 | 11 | for grp in divide_group(recipient_list, 100): 12 | try: 13 | connection = get_connection( 14 | username=None, 15 | password=None, 16 | fail_silently=fail_silently, 17 | ) 18 | mail = EmailMultiAlternatives(subject, bcc=grp, connection=connection) 19 | mail.attach_alternative(html_message, 'text/html') 20 | mail.send() 21 | except: 22 | traceback.print_exc() 23 | time.sleep(30) 24 | -------------------------------------------------------------------------------- /templates/account/base.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | {% block title %}账户设置 - {% endblock %} 3 | 4 | {% block page_header %}账户设置{% endblock %} 5 | 6 | {% block content %} 7 | 8 | 15 | 16 | {% block account_setting %} 17 | {% endblock %} 18 | 19 | {% endblock %} 20 | -------------------------------------------------------------------------------- /.github/workflows/pythonapp.yml: -------------------------------------------------------------------------------- 1 | name: EOJ Test 2 | 3 | on: [pull_request] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: Set up Python 3.7 13 | uses: actions/setup-python@v1 14 | with: 15 | python-version: 3.7 16 | - name: Install dependencies 17 | run: | 18 | python -m pip install --upgrade pip 19 | pip install -r requirements.txt 20 | cp eoj3/local_settings.example.py eoj3/local_settings.py 21 | - name: Lint with pylint 22 | run: | 23 | pip install pylint 24 | pylint --rcfile pylintrc account api backstage blog contest dispatcher eoj3 filemanager home migrate notification paste polygon problem scripts submission tests utils 25 | -------------------------------------------------------------------------------- /problem/testlib/validator/nval.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Validates that the first line contains the integer between 1 and 10^5, inclusive. 3 | * The second line should contains space-separated sequence of integers between -10^15 and 10^15, inclusive. 4 | * Also validates that file ends with EOLN and EOF. 5 | */ 6 | 7 | #include "testlib.h" 8 | 9 | using namespace std; 10 | 11 | int main(int argc, char* argv[]) 12 | { 13 | registerValidation(argc, argv); 14 | 15 | int n = inf.readInt(1, 100000, "n"); 16 | inf.readEoln(); 17 | 18 | for (int i = 0; i < n; i++) 19 | { 20 | inf.readLong(-1000000000000000LL, 1000000000000000LL, format("a[%d]", i + 1)); 21 | 22 | if (i + 1 < n) 23 | inf.readSpace(); 24 | } 25 | inf.readEoln(); 26 | 27 | inf.readEof(); 28 | return 0; 29 | } 30 | -------------------------------------------------------------------------------- /problem/testlib/interactor/interactor-a-plus-b.cpp: -------------------------------------------------------------------------------- 1 | #include "testlib.h" 2 | #include 3 | 4 | using namespace std; 5 | 6 | int main(int argc, char* argv[]) 7 | { 8 | setName("Interactor A+B"); 9 | registerInteraction(argc, argv); 10 | 11 | // reads number of queries from test (input) file 12 | int n = inf.readInt(); 13 | for (int i = 0; i < n; i++) 14 | { 15 | // reads query from test (input) file 16 | int a = inf.readInt(); 17 | int b = inf.readInt(); 18 | 19 | // writes query to the solution, endl makes flush 20 | cout << a << " " << b << endl; 21 | 22 | // writes output file to be verified by checker later 23 | tout << ouf.readInt() << endl; 24 | } 25 | 26 | // just message 27 | quitf(_ok, "%d queries processed", n); 28 | } 29 | -------------------------------------------------------------------------------- /templates/error/403.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'html_base.jinja2' %} 2 | 3 | {% block css %} 4 | 15 | {% endblock %} 16 | 17 | {% block _content %} 18 |
19 | 20 |
21 | 22 |

23 | 访问受限 24 | 25 |

26 |
27 |
28 | 29 | 34 | {% endblock %} 35 | -------------------------------------------------------------------------------- /backstage/site/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import redirect 2 | from django.views.generic import TemplateView 3 | 4 | from utils.site_settings import site_settings_set, SiteSettings 5 | from ..base_views import BaseBackstageMixin 6 | 7 | 8 | class SiteSettingsUpdate(BaseBackstageMixin, TemplateView): 9 | template_name = 'backstage/site/site.jinja2' 10 | 11 | def get_context_data(self, **kwargs): 12 | context = super(SiteSettingsUpdate, self).get_context_data(**kwargs) 13 | context['site_settings'] = SiteSettings.objects.all() 14 | return context 15 | 16 | def post(self, request): 17 | key, value = request.POST['key'], request.POST['value'] 18 | if value == '': 19 | SiteSettings.objects.filter(key=key).delete() 20 | else: 21 | site_settings_set(key, value) 22 | return redirect(request.POST['next']) 23 | -------------------------------------------------------------------------------- /templates/blog/reward_list.jinja2: -------------------------------------------------------------------------------- 1 | {# has a weird name; it means personal profile... #} 2 | {# UPD: this is a personal blog page now #} 3 | 4 | {% extends 'base.jinja2' %} 5 | {% from 'components/profile_card.jinja2' import profile_card with context %} 6 | {% from 'components/blog_preview.jinja2' import blog_preview %} 7 | 8 | {% block title %}悬赏 - {% endblock %} 9 | 10 | {% block page_header %}{{ profile.get_username_display() }}{% endblock %} 11 | 12 | {% block content %} 13 | 14 |
15 | 16 |
17 | 18 |
19 | {% if blog_list %} 20 | {% for blog in blog_list %} 21 | {{ blog_preview(blog) }} 22 | {% endfor %} 23 | {% endif %} 24 |
25 |
26 | 27 |
28 | 29 | {% endblock %} 30 | -------------------------------------------------------------------------------- /static/gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'), 2 | less = require('gulp-less'), 3 | cssmin = require('gulp-cssmin'), 4 | plumber = require('gulp-plumber'), 5 | rename = require('gulp-rename'); 6 | 7 | gulp.task('watch', function () { 8 | gulp.watch('./less/app.less', ['less-dev']); 9 | }); 10 | 11 | gulp.task('less-dev', function() { 12 | gulp.src('./less/app.less') 13 | .pipe(less()) 14 | .pipe(rename({ 15 | suffix: '.min' 16 | })) 17 | .pipe(gulp.dest('./css')) 18 | }); 19 | 20 | gulp.task('less', function () { 21 | gulp.src('./less/app.less') 22 | .pipe(plumber()) 23 | .pipe(less()) 24 | .pipe(gulp.dest('./css/')) 25 | .pipe(cssmin()) 26 | .pipe(rename({ 27 | suffix: '.min' 28 | })) 29 | .pipe(gulp.dest('./css')) 30 | }); 31 | 32 | gulp.task('default', ['less', 'watch']); -------------------------------------------------------------------------------- /problem/testlib/generator/swgen.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Outputs random token. 3 | * 4 | * Token contains latin letters and digits and have length 5 | * between 1 and 1000 characters, inclusive. 6 | * 7 | * To generate different values, call "swgen.exe ". 8 | * See "iwgen.cpp" and "wnext()" documentation for details. 9 | * For example, "swgen.exe -1000" generates short strings and "swgen.exe 1000" 10 | * generates long strings. 11 | * 12 | * It is typical behaviour of testlib generator to setup randseed by command line. 13 | */ 14 | 15 | #include "testlib.h" 16 | #include 17 | 18 | using namespace std; 19 | 20 | int main(int argc, char* argv[]) 21 | { 22 | registerGen(argc, argv, 1); 23 | 24 | int length = rnd.wnext(1, 1000, atoi(argv[1])); 25 | cout << rnd.next("[a-zA-Z0-9]{1,%d}", length) << endl; 26 | 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /problem/testlib/validator/validate-using-testset-and-group.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Validates that input depenging on testset and group. 3 | */ 4 | 5 | #include "testlib.h" 6 | #include 7 | 8 | using namespace std; 9 | 10 | int main(int argc, char* argv[]) 11 | { 12 | registerValidation(argc, argv); 13 | 14 | int n, m; 15 | 16 | if (validator.testset() == "pretests") 17 | { 18 | n = inf.readInt(1, 10, "n"); 19 | inf.readSpace(); 20 | m = inf.readInt(1, 10, "m"); 21 | } 22 | else 23 | { 24 | n = inf.readInt(1, 100, "n"); 25 | inf.readSpace(); 26 | m = inf.readInt(1, 100, "m"); 27 | } 28 | 29 | if (validator.group() == "even-n-and-m") 30 | { 31 | ensure(n % 2 == 0); 32 | ensure(m % 2 == 0); 33 | } 34 | 35 | inf.readEoln(); 36 | inf.readEof(); 37 | } 38 | -------------------------------------------------------------------------------- /utils/markdown3/__init__.py: -------------------------------------------------------------------------------- 1 | import markdown 2 | from django.shortcuts import HttpResponse 3 | 4 | from . import mdx_downheader 5 | from . import semantic 6 | 7 | 8 | def convert(text): 9 | md = markdown.Markdown( 10 | extensions=[mdx_downheader.makeExtension(levels=2), 11 | # mdx_math.makeExtension(enable_dollar_delimiter=True, add_preview=False), 12 | 'fenced_code', 13 | 'codehilite', 14 | 'markdown.extensions.attr_list', 15 | 'nl2br', 16 | 'tables', 17 | 'markdown.extensions.smarty' 18 | ] 19 | ) 20 | return semantic.semantic_processor(md.convert(text)) 21 | 22 | 23 | def markdown_convert_api(request): 24 | from utils.jinja2.filters import xss_filter 25 | return HttpResponse(xss_filter(convert(request.POST.get('text', '')))) 26 | -------------------------------------------------------------------------------- /utils/middleware/close_site_middleware.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | from utils.site_settings import is_site_closed 4 | 5 | 6 | class CloseSiteException(Exception): 7 | pass 8 | 9 | 10 | class CloseSiteMiddleware(object): 11 | 12 | def __init__(self, get_response): 13 | self.get_response = get_response 14 | 15 | def __call__(self, request): 16 | response = self.get_response(request) 17 | return response 18 | 19 | @staticmethod 20 | def process_view(request, view_func, view_args, view_kwargs): 21 | force_closed = view_kwargs.pop('force_closed', False) 22 | try: 23 | if force_closed and is_site_closed(request): 24 | raise CloseSiteException 25 | return view_func(request, *view_args, **view_kwargs) 26 | except CloseSiteException: 27 | return render(request, 'error/closed.jinja2', status=418) 28 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "eoj3.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError: 10 | # The above import may fail for some other reason. Ensure that the 11 | # issue is really that Django is missing to avoid masking other 12 | # exceptions on Python 2. 13 | try: 14 | import django 15 | except ImportError: 16 | raise ImportError( 17 | "Couldn't import Django. Are you sure it's installed and " 18 | "available on your PYTHONPATH environment variable? Did you " 19 | "forget to activate a virtual environment?" 20 | ) 21 | raise 22 | execute_from_command_line(sys.argv) 23 | -------------------------------------------------------------------------------- /templates/problem/standings.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'problem/base.jinja2' %} 2 | 3 | {% block problem_content %} 4 | 5 |

你的排名:{{ my_rank }}

6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | {% for rank in rank_list %} 18 | 19 | 20 | 21 | 22 | 23 | 24 | {% endfor %} 25 | 26 |
#解决EMB
{{ page_obj.start_index() + loop.index0 }}{{ username_display(rank) }}{{ rank.solved }}{{ rank.score | round(1) }}
27 | 28 | {{ my_paginator() }} 29 | 30 | {% endblock %} 31 | -------------------------------------------------------------------------------- /blog/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from . import views 3 | 4 | app_name = "blog" 5 | 6 | urlpatterns = [ 7 | url(r'^(?P\d+)/$', views.GenericView.as_view(), name='index'), 8 | url(r'^create/$', views.BlogCreate.as_view(), name='create'), 9 | url(r'^update/(?P\d+)/$', views.BlogUpdate.as_view(), name='update'), 10 | url(r'^entry/(?P\d+)/$', views.BlogView.as_view(), name='detail'), 11 | url(r'^entry/(?P\d+)/revision/(?P\d+)/$', views.BlogRevisionView.as_view(), name='revision_detail'), 12 | url(r'^entry/(?P\d+)/comment/$', views.BlogAddComment.as_view(), name='add_comment'), 13 | url(r'^entry/(?P\d+)/comment/(?P\d+)/delete/$', views.BlogDeleteComment.as_view(), 14 | name='delete_comment'), 15 | url(r'^feedback/', views.LikeBlog.as_view(), name='like'), 16 | url(r'^reward/$', views.RewardView.as_view(), name='reward') 17 | ] 18 | -------------------------------------------------------------------------------- /home/api_views.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import authenticate, login 2 | from rest_framework.response import Response 3 | from rest_framework.views import APIView 4 | 5 | from account.models import User 6 | 7 | 8 | class RegisterAPI(APIView): 9 | def post(self, request): 10 | email = request.data.get('email') 11 | username = request.data.get('username') 12 | password = request.data.get('password') 13 | user = User.objects.create(email=email, username=username) 14 | user.set_password(password) 15 | user.save() 16 | return Response({'username': user.username}) 17 | 18 | 19 | class LoginAPI(APIView): 20 | def post(self, request): 21 | username = request.data.get('username') 22 | password = request.data.get('password') 23 | user = authenticate(username=username, password=password) 24 | login(request, user) 25 | return Response({'username': user.username}) 26 | -------------------------------------------------------------------------------- /templates/components/rsa_encrypt.jinja2: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /templates/problem/base.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | {% set hide_header = True %} 3 | 4 | {% block page_header %}题目集{% endblock %} 5 | 6 | {% block content %} 7 | 8 | 16 | 17 | {% block problem_content %} 18 | {% endblock %} 19 | {% endblock %} -------------------------------------------------------------------------------- /paste/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | 3 | from paste.models import Paste 4 | from utils.multiple_choice_field import UserSelectMultiple 5 | 6 | 7 | class PasteForm(forms.ModelForm): 8 | class Meta: 9 | model = Paste 10 | fields = ['code', 'lang', 'public_access', 'expire_after'] 11 | 12 | invited_users = UserSelectMultiple(required=False) 13 | 14 | def clean_code(self): 15 | if len(self.cleaned_data["code"]) > 1048576: 16 | raise forms.ValidationError("The upper limit for the code is 1MB.") 17 | return self.cleaned_data["code"] 18 | 19 | 20 | class AnonymousPasteForm(forms.ModelForm): 21 | class Meta: 22 | model = Paste 23 | fields = ['code', 'lang', 'expire_after'] 24 | 25 | def clean_code(self): 26 | if len(self.cleaned_data["code"]) > 1048576: 27 | raise forms.ValidationError("The upper limit for the code is 1MB.") 28 | return self.cleaned_data["code"] 29 | -------------------------------------------------------------------------------- /problem/testlib/checker/yesno.cpp: -------------------------------------------------------------------------------- 1 | #include "testlib.h" 2 | #include 3 | 4 | using namespace std; 5 | 6 | const string YES = "YES"; 7 | const string NO = "NO"; 8 | 9 | int main(int argc, char * argv[]) 10 | { 11 | setName("%s", (YES + " or " + NO + " (case insensetive)").c_str()); 12 | registerTestlibCmd(argc, argv); 13 | 14 | std::string ja = upperCase(ans.readWord()); 15 | std::string pa = upperCase(ouf.readWord()); 16 | 17 | if (ja != YES && ja != NO) 18 | quitf(_fail, "%s or %s expected in answer, but %s found", YES.c_str(), NO.c_str(), compress(ja).c_str()); 19 | 20 | if (pa != YES && pa != NO) 21 | quitf(_pe, "%s or %s expected, but %s found", YES.c_str(), NO.c_str(), compress(pa).c_str()); 22 | 23 | if (ja != pa) 24 | quitf(_wa, "expected %s, found %s", compress(ja).c_str(), compress(pa).c_str()); 25 | 26 | quitf(_ok, "answer is %s", ja.c_str()); 27 | } 28 | -------------------------------------------------------------------------------- /account/color.py: -------------------------------------------------------------------------------- 1 | from django.db import transaction 2 | 3 | from account.models import User 4 | 5 | 6 | def update_color(): 7 | colors = [(2100, 'legendary'), 8 | (1800, 'red'), 9 | (1700, 'orange'), 10 | (1600, 'purple'), 11 | (1500, 'blue'), 12 | (1350, 'teal'), 13 | (0, 'green')] 14 | with transaction.atomic(): 15 | User.objects.all().update(magic='') 16 | User.objects.filter(is_superuser=True).update(magic='grey') 17 | User.objects.filter(is_staff=True).update(magic='grey') 18 | user_list = list(User.objects.filter(rating__gt=0, is_staff=False, is_superuser=False).order_by("-rating")) 19 | for user in user_list: 20 | select_color = 0 21 | while select_color < 6 and user.rating < colors[select_color][0]: 22 | select_color += 1 23 | user.magic = colors[select_color][1] 24 | user.save(update_fields=['magic']) 25 | -------------------------------------------------------------------------------- /problem/testlib/checker/rcmp6.cpp: -------------------------------------------------------------------------------- 1 | #include "testlib.h" 2 | #include 3 | 4 | using namespace std; 5 | 6 | const double EPS = 1E-6; 7 | 8 | int main(int argc, char * argv[]) 9 | { 10 | setName("compare two sequences of doubles, max absolute or relative error = %.7f", EPS); 11 | registerTestlibCmd(argc, argv); 12 | 13 | int n = 0; 14 | double j = 0, p = 0; 15 | 16 | while (!ans.seekEof()) 17 | { 18 | n++; 19 | j = ans.readDouble(); 20 | p = ouf.readDouble(); 21 | if (!doubleCompare(j, p, EPS)) 22 | { 23 | quitf(_wa, "%d%s numbers differ - expected: '%.7f', found: '%.7f', error = '%.7f'", 24 | n, englishEnding(n).c_str(), j, p, doubleDelta(j, p)); 25 | } 26 | } 27 | 28 | if (n == 1) 29 | quitf(_ok, "found '%.7f', expected '%.7f', error '%.7f'", p, j, doubleDelta(j, p)); 30 | 31 | quitf(_ok, "%d numbers", n); 32 | } 33 | -------------------------------------------------------------------------------- /polygon/templates/polygon/runs.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'polygon/polygon_base.jinja2' %} 2 | 3 | {% block title %}Runs - {% endblock %} 4 | 5 | {% block content %} 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | {% for run in runs_list %} 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | {% endfor %} 27 | 28 |
#WhenStatusLabelMessage
{{ run.id }}{{ run.create_time | date('Y-m-d H:i:s') }}{{ run.get_status_display() }}{{ run.label }}Reveal
29 | {{ my_paginator() }} 30 | 31 | {% endblock %} 32 | -------------------------------------------------------------------------------- /problem/testlib/checker/rcmp4.cpp: -------------------------------------------------------------------------------- 1 | #include "testlib.h" 2 | #include 3 | 4 | using namespace std; 5 | 6 | const double EPS = 1E-4; 7 | 8 | int main(int argc, char * argv[]) 9 | { 10 | setName("compare two sequences of doubles, max absolute or relative error = %.5f", EPS); 11 | registerTestlibCmd(argc, argv); 12 | 13 | int n = 0; 14 | double j = 0, p = 0; 15 | 16 | while (!ans.seekEof()) 17 | { 18 | n++; 19 | j = ans.readDouble(); 20 | p = ouf.readDouble(); 21 | if (!doubleCompare(j, p, EPS)) 22 | { 23 | quitf(_wa, "%d%s numbers differ - expected: '%.5f', found: '%.5f', error = '%.5f'", 24 | n, englishEnding(n).c_str(), j, p, doubleDelta(j, p)); 25 | } 26 | } 27 | 28 | if (n == 1) 29 | quitf(_ok, "found '%.5f', expected '%.5f', error '%.5f'", p, j, doubleDelta(j, p)); 30 | 31 | quitf(_ok, "%d numbers", n); 32 | } 33 | -------------------------------------------------------------------------------- /problem/testlib/generator/multigen.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * It is another type of generators. It writes several files named 3 | * as test indices. 4 | * 5 | * For example, this generator writes 10 files (tests) from 1 to 10. 6 | * This type of generators supported by Polygon too, but I believe 7 | * that stdout-generators are more preferred. 8 | * 9 | * The generator for A+B problem, generates 10 tests where each 10 | * number is between 1 and 100, and tests grow with indices. 11 | */ 12 | 13 | #include "testlib.h" 14 | #include 15 | 16 | using namespace std; 17 | 18 | void writeTest(int test) 19 | { 20 | startTest(test); 21 | 22 | cout << rnd.next(1, test * test) 23 | << " " << rnd.next(1, test * test) << endl; 24 | } 25 | 26 | int main(int argc, char* argv[]) 27 | { 28 | registerGen(argc, argv, 1); 29 | 30 | for (int i = 1; i <= 10; i++) 31 | writeTest(i); 32 | 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /problem/testlib/checker/rcmp9.cpp: -------------------------------------------------------------------------------- 1 | #include "testlib.h" 2 | #include 3 | 4 | using namespace std; 5 | 6 | const double EPS = 1E-9; 7 | 8 | int main(int argc, char * argv[]) 9 | { 10 | setName("compare two sequences of doubles, max absolute or relative error = %.10f", EPS); 11 | registerTestlibCmd(argc, argv); 12 | 13 | int n = 0; 14 | double j = 0, p = 0; 15 | 16 | while (!ans.seekEof()) 17 | { 18 | n++; 19 | j = ans.readDouble(); 20 | p = ouf.readDouble(); 21 | if (!doubleCompare(j, p, EPS)) 22 | { 23 | quitf(_wa, "%d%s numbers differ - expected: '%.10f', found: '%.10f', error = '%.10f'", 24 | n, englishEnding(n).c_str(), j, p, doubleDelta(j, p)); 25 | } 26 | } 27 | 28 | if (n == 1) 29 | quitf(_ok, "found '%.10f', expected '%.10f', error '%.10f'", p, j, doubleDelta(j, p)); 30 | 31 | quitf(_ok, "%d numbers", n); 32 | } 33 | -------------------------------------------------------------------------------- /templates/contest/my_status.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'contest/base.jinja2' %} 2 | {% from 'components/status.jinja2' import status with context %} 3 | 4 | {% block contest_title %}我的提交 - {% endblock %} 5 | {% block contest_content %} 6 | 7 | 20 | 21 | {% include 'components/message.jinja2' %} 22 | 23 |
带 * 的是非正式提交
24 | 25 | {{ status() }} 26 | 27 | {% endblock %} -------------------------------------------------------------------------------- /utils/markdown3/semantic.py: -------------------------------------------------------------------------------- 1 | import traceback 2 | 3 | import markupsafe 4 | from bs4 import BeautifulSoup 5 | 6 | RULES = { 7 | # tag: (class list, [default, optional]) 8 | 'table': (['ui', 'table', 'center', 'aligned', 'celled'], []), 9 | 'img': (['ui', 'image', 'centered'], ['large', 'medium', 'fluid', 'mini', 'tiny', 'small', 'big', 'huge', 'massive']) 10 | } 11 | 12 | 13 | def semantic_processor(text): 14 | try: 15 | soup = BeautifulSoup(str(text), "html.parser") 16 | for child in soup.recursiveChildGenerator(): 17 | if child.name in RULES.keys(): 18 | rule = RULES[child.name] 19 | child.attrs.setdefault('class', []) 20 | child.attrs['class'].extend(rule[0]) 21 | if rule[1] and all(size not in child.attrs['class'] for size in rule[1]): 22 | child.attrs['class'].append(rule[1][0]) 23 | return markupsafe.Markup(soup) 24 | except: 25 | traceback.print_exc() 26 | return text 27 | -------------------------------------------------------------------------------- /problem/testlib/checker/fcmp.cpp: -------------------------------------------------------------------------------- 1 | #include "testlib.h" 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | int main(int argc, char * argv[]) 9 | { 10 | setName("compare files as sequence of lines"); 11 | registerTestlibCmd(argc, argv); 12 | 13 | std::string strAnswer; 14 | 15 | int n = 0; 16 | while (!ans.eof()) 17 | { 18 | std::string j = ans.readString(); 19 | 20 | if (j == "" && ans.eof()) 21 | break; 22 | 23 | strAnswer = j; 24 | std::string p = ouf.readString(); 25 | 26 | n++; 27 | 28 | if (j != p) 29 | quitf(_wa, "%d%s lines differ - expected: '%s', found: '%s'", n, englishEnding(n).c_str(), compress(j).c_str(), compress(p).c_str()); 30 | } 31 | 32 | if (n == 1) 33 | quitf(_ok, "single line: '%s'", compress(strAnswer).c_str()); 34 | 35 | quitf(_ok, "%d lines", n); 36 | } 37 | -------------------------------------------------------------------------------- /templates/contest/activity/confirmation.jinja2: -------------------------------------------------------------------------------- 1 |

{{ participant.real_name }},您好!

2 | 3 |

感谢报名参加 {{ activity.title }}

4 | 5 | {% if participant.is_deleted %} 6 |

您已放弃这次比赛。您收到这封邮件是为了确认您的「放弃」操作。如果存疑,请联系下面的邮箱。

7 | {% endif %} 8 | 9 | {{ activity.description | markdown | safe }} 10 | 11 |

您提供的报名信息如下:

12 | 13 |
    14 |
  • 学校:{{ participant.school }}
  • 15 |
  • 学号:{{ participant.student_id }}
  • 16 |
  • 邮箱:{{ participant.email }}
  • 17 |
  • 电话:{{ participant.phone }}
  • 18 |
  • 专业:{{ participant.get_major_display() }}
  • 19 |
  • 性别:{{ participant.get_gender_display() }}
  • 20 |
  • 毕业年份:{{ participant.graduate_year }}
  • 21 |
22 | 23 |

请尽快点击下面的确认链接确认报名!逾期未确认视为自动放弃。

24 | 25 |

{{ link | safe }}

26 | 27 |

如果上述报名信息有误,请联系:acmsupport@admin.ecnu.edu.cn

28 | 29 |

EOJ Team

30 | -------------------------------------------------------------------------------- /static/less/app.less: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | @import '../node_modules/semantic-ui-less/semantic'; 3 | @import 'homepage'; 4 | @import 'layout'; 5 | @import 'typography'; 6 | @import 'problem'; 7 | @import 'code'; 8 | @import 'simplemde'; 9 | @import 'heatmap'; 10 | @import 'username'; 11 | 12 | // typed.js 13 | .typed-cursor { 14 | opacity: 1; 15 | -webkit-animation: blink 0.7s infinite; 16 | -moz-animation: blink 0.7s infinite; 17 | -ms-animation: blink 0.7s infinite; 18 | -o-animation: blink 0.7s infinite; 19 | animation: blink 0.7s infinite; 20 | } 21 | 22 | .float-right { 23 | float: right !important; 24 | } 25 | 26 | // problem 27 | 28 | .property { 29 | p:first-child { 30 | margin-top: 1rem; 31 | } 32 | p:not(:last-child) { 33 | margin-bottom: .2rem; 34 | } 35 | } 36 | 37 | .blog-like-link { 38 | cursor: pointer; 39 | display: inline-block; 40 | margin: 0 .75em 0 0; 41 | color: rgba(0, 0, 0, .4); 42 | font-size: .875em; 43 | } -------------------------------------------------------------------------------- /api/views/problem.py: -------------------------------------------------------------------------------- 1 | from django.template import loader, Context 2 | from rest_framework import serializers 3 | from rest_framework.generics import RetrieveAPIView 4 | 5 | from problem.models import Problem 6 | 7 | 8 | class ProblemSerializer(serializers.ModelSerializer): 9 | class Meta: 10 | model = Problem 11 | fields = ("id", "title", "tags", "statement", "level", "ac_user_count", "total_user_count", "ac_count", 12 | "total_count", "reward") 13 | 14 | tags = serializers.SerializerMethodField() 15 | statement = serializers.SerializerMethodField() 16 | 17 | def get_tags(self, problem): 18 | return [tag.name for tag in problem.tags] 19 | 20 | def get_statement(self, problem): 21 | t = loader.get_template('api/problem.jinja2') 22 | return t.render(Context({"problem": problem})) 23 | 24 | 25 | class ProblemView(RetrieveAPIView): 26 | queryset = Problem.objects.filter(visible=True) 27 | serializer_class = ProblemSerializer 28 | -------------------------------------------------------------------------------- /static/js/longpoll.js: -------------------------------------------------------------------------------- 1 | function longPoll(url, callback, restInterval, stopCondition, timeout) { 2 | // when stopCondition(something) is true, callback will be called and no more poll request 3 | $.ajax({ 4 | url: url, 5 | dataType: "json", 6 | success: function (data) { 7 | if (stopCondition(data)) { 8 | callback(data); 9 | } else { 10 | setTimeout(function () { 11 | longPoll(url, callback, restInterval, stopCondition, timeout); 12 | }, restInterval); 13 | } 14 | }, 15 | timeout: timeout || 30000 16 | }); 17 | } 18 | 19 | function longPollUntilForever(url, callback, restInterval, timeout) { 20 | $.ajax({ 21 | url: url, 22 | success: callback, 23 | dataType: "json", 24 | complete: function () { 25 | setTimeout(function () { 26 | longPollUntilForever(url, callback, restInterval, timeout); 27 | }, restInterval); 28 | }, 29 | timeout: timeout || 30000 30 | }); 31 | } -------------------------------------------------------------------------------- /utils/middleware/globalrequestmiddleware.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | 4 | class GlobalRequestMiddleware(object): 5 | _threadmap = {} 6 | 7 | def __init__(self, get_response): 8 | self.get_response = get_response 9 | 10 | def __call__(self, request): 11 | self.process_request(request) 12 | response = self.get_response(request) 13 | return self.process_response(request, response) 14 | 15 | @classmethod 16 | def get_current_request(cls): 17 | return cls._threadmap[threading.get_ident()] 18 | 19 | def process_request(self, request): 20 | self._threadmap[threading.get_ident()] = request 21 | 22 | def process_exception(self, request, exception): 23 | try: 24 | del self._threadmap[threading.get_ident()] 25 | except KeyError: 26 | pass 27 | 28 | def process_response(self, request, response): 29 | try: 30 | del self._threadmap[threading.get_ident()] 31 | except KeyError: 32 | pass 33 | return response 34 | -------------------------------------------------------------------------------- /templates/blog/generic.jinja2: -------------------------------------------------------------------------------- 1 | {# has a weird name; it means personal profile... #} 2 | {# UPD: this is a personal blog page now #} 3 | 4 | {% extends 'base.jinja2' %} 5 | {% from 'components/profile_card.jinja2' import profile_card with context %} 6 | {% from 'components/blog_preview.jinja2' import blog_preview %} 7 | 8 | {% block title %}博客 - {% endblock %} 9 | 10 | {% block page_header %}{{ profile.get_username_display() }}{% endblock %} 11 | 12 | {% block content %} 13 | 14 |
15 | 16 |
17 | 18 | {% if is_author %}写博客{% endif %} 19 |
20 | {% if blog_list %} 21 | {% for blog in blog_list %} 22 | {{ blog_preview(blog) }} 23 | {% endfor %} 24 | {% endif %} 25 |
26 |
27 | 28 |
29 | 30 | 31 | {% endblock %} 32 | -------------------------------------------------------------------------------- /problem/testlib/checker/hcmp.cpp: -------------------------------------------------------------------------------- 1 | #include "testlib.h" 2 | 3 | #include 4 | 5 | using namespace std; 6 | 7 | pattern pnum("0|-?[1-9][0-9]*"); 8 | 9 | bool isNumeric(const string& p) 10 | { 11 | return pnum.matches(p); 12 | } 13 | 14 | int main(int argc, char * argv[]) 15 | { 16 | setName("compare two signed huge integers"); 17 | registerTestlibCmd(argc, argv); 18 | 19 | string ja = ans.readWord(); 20 | string pa = ouf.readWord(); 21 | 22 | if (!isNumeric(ja)) 23 | quitf(_fail, "%s is not a valid integer", compress(ja).c_str()); 24 | 25 | if (!ans.seekEof()) 26 | quitf(_fail, "expected exactly one token in the answer file"); 27 | 28 | if (!isNumeric(pa)) 29 | quitf(_pe, "%s is not a valid integer", compress(pa).c_str()); 30 | 31 | if (ja != pa) 32 | quitf(_wa, "expected '%s', found '%s'", compress(ja).c_str(), compress(pa).c_str()); 33 | 34 | quitf(_ok, "answer is '%s'", compress(ja).c_str()); 35 | } 36 | -------------------------------------------------------------------------------- /templates/backstage/account/school.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'backstage/base.jinja2' %} 2 | {% block content_header %} 3 | Schools 4 | {% endblock %} 5 | 6 | {% block backstage_content %} 7 | 8 | {% include 'components/message.jinja2' %} 9 | 10 | 11 | 12 | 13 | 18 | 19 | 20 | 21 | {% for school in school_list %} 22 | 23 | 24 | 25 | 26 | 27 | 28 | {% endfor %} 29 | 30 |
14 | 15 | Add School 16 | 17 |
{{ school.pk }}{{ school.name }}{{ school.abbr }}{{ school.alias }}
31 | 32 | {% endblock %} 33 | -------------------------------------------------------------------------------- /templates/update_log.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block title %}更新日志 - {% endblock %} 4 | 5 | {% block page_header %}更新日志{% endblock %} 6 | 7 | {% block content %} 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | {% for log in log_list %} 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | {% endfor %} 31 | 32 |
创建时间类型优先级Polygon内容签名
{{ log.create_time | date('Y-m-d H:i:s') }}{{ log.get_log_type_display() }}{{ log.get_priority_display() }}{{ log.is_about_polygon }}{{ log.content }}{% if log.created_by %}{{ username_display(log.created_by) }}{% endif %}
33 | {% endblock %} 34 | -------------------------------------------------------------------------------- /polygon/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url, include 2 | 3 | import polygon.views as v 4 | 5 | app_name = "polygon" 6 | 7 | urlpatterns = [ 8 | url(r'^$', v.home_view, name='home'), 9 | url(r'^register/$', v.register_view, name='register'), 10 | url(r'^rejudge/(?P\d+)/$', v.RejudgeSubmission.as_view(), name='rejudge_submission'), 11 | url(r'^submission/(?P\d+)/hidden/$', v.ToggleSubmissionHidden.as_view(), name='toggle_submission_hidden'), 12 | url(r'^problem/', include('polygon.problem2.urls')), 13 | url(r'^contest/', include('polygon.contest.urls')), 14 | url(r'^runs/$', v.RunsList.as_view(), name='runs'), 15 | url(r'^runs/message/(?P\d+)/$', v.RunMessageView.as_view(), name='run_message'), 16 | url(r'^packages/$', v.PackageView.as_view(), name='packages'), 17 | url(r'^packages/create/$', v.PackageCreate.as_view()), 18 | url(r'^packages/(?P\d+)/log/$', v.PackageLogsDownload.as_view()), 19 | url(r'^packages/(?P\d+)/download/$', v.PackageDownload.as_view()), 20 | ] 21 | -------------------------------------------------------------------------------- /templates/problem/detail/base.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block title %}Problem #{{ problem.pk }} - {% endblock %} 4 | 5 | {% block page_header %}{{ problem.pk }}. {{ problem.title }}{% endblock %} 6 | 7 | {% block content %} 8 | 9 | 19 | 20 | {% block problem_content %} 21 | {% endblock %} 22 | 23 | {% endblock %} 24 | -------------------------------------------------------------------------------- /templates/components/modal.jinja2: -------------------------------------------------------------------------------- 1 | {% macro modal(title, size="tiny", action=None, id="modal", negative_button_name="关闭", button_name="确定") %} 2 | 28 | {% endmacro %} -------------------------------------------------------------------------------- /account/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | 3 | from utils.site_settings import force_closed 4 | from . import views 5 | 6 | app_name = "account" 7 | 8 | urlpatterns = [ 9 | url(r'^settings/profile/$', views.UpdateProfileView.as_view(), name='profile', kwargs=force_closed()), 10 | url(r'^settings/security/$', views.my_password_change, name='security', kwargs=force_closed()), 11 | url(r'^settings/preference/$', views.UpdatePreferencesView.as_view(), name='preference', kwargs=force_closed()), 12 | url(r'^settings/username/update/$', views.ChangeUsernameView.as_view(), name='change_username'), 13 | url(r'^password_reset/$', views.my_password_reset, name='reset_password'), 14 | url(r'^password_reset_done/$', views.my_password_reset_done, name='password_reset_done'), 15 | url(r'^reset/(?P[0-9A-Za-z_\-]+)/(?P[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', 16 | views.my_password_reset_confirm, name='password_reset_confirm'), 17 | url(r'^ban/(?P[0-9]+)/$', views.BanAccount.as_view(), name='ban_account'), 18 | ] 19 | -------------------------------------------------------------------------------- /blog/forms.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from captcha.fields import CaptchaField 4 | from django import forms 5 | 6 | from account.models import User 7 | from blog.models import Blog 8 | from utils.jinja2.globals import username_display 9 | 10 | 11 | def generate_username_link(matchobj): 12 | id = matchobj.group(1) 13 | try: 14 | user = User.objects.get(pk=id) 15 | return username_display(None, user) 16 | except: 17 | raise forms.ValidationError("用户 ID %d 不存在" % id) 18 | 19 | 20 | class BlogEditForm(forms.ModelForm): 21 | captcha = CaptchaField(label="小学数学题") 22 | 23 | class Meta: 24 | model = Blog 25 | fields = ['title', 'text', 'visible', 'hide_revisions'] 26 | error_messages = { 27 | } 28 | help_texts = { 29 | 'text': "支持 Markdown 和 MathJax,使用 $e^x$ 来写公式。" 30 | } 31 | widgets = { 32 | 'text': forms.Textarea(attrs={'class': 'markdown'}) 33 | } 34 | 35 | def clean_text(self): 36 | text = self.cleaned_data["text"] 37 | return re.sub(r"\[user:(\d+)\]", generate_username_link, text) 38 | -------------------------------------------------------------------------------- /problem/testlib/checker/wcmp.cpp: -------------------------------------------------------------------------------- 1 | #include "testlib.h" 2 | 3 | using namespace std; 4 | 5 | int main(int argc, char * argv[]) 6 | { 7 | setName("compare sequences of tokens"); 8 | registerTestlibCmd(argc, argv); 9 | 10 | int n = 0; 11 | string j, p; 12 | 13 | while (!ans.seekEof() && !ouf.seekEof()) 14 | { 15 | n++; 16 | 17 | ans.readWordTo(j); 18 | ouf.readWordTo(p); 19 | 20 | if (j != p) 21 | quitf(_wa, "%d%s words differ - expected: '%s', found: '%s'", n, englishEnding(n).c_str(), compress(j).c_str(), compress(p).c_str()); 22 | } 23 | 24 | if (ans.seekEof() && ouf.seekEof()) 25 | { 26 | if (n == 1) 27 | quitf(_ok, "\"%s\"", compress(j).c_str()); 28 | else 29 | quitf(_ok, "%d tokens", n); 30 | } 31 | else 32 | { 33 | if (ans.seekEof()) 34 | quitf(_wa, "Participant output contains extra tokens"); 35 | else 36 | quitf(_wa, "Unexpected EOF in the participants output"); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /templates/contest/contest_ratings.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block title %}比赛积分 - {% endblock %} 4 | 5 | {% block page_header %}比赛积分{% endblock %} 6 | 7 | {% block content %} 8 | 9 |

10 | : 1800. : 1700. : 1600. : 1500. : 1350. 绿: 0. : Admin. 11 |

12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | {% for rating_user in global_rating %} 21 | 22 | 23 | 24 | 25 | {% endfor %} 26 | 27 |
用户名积分
{{ username_display(rating_user) }}{{ rating_user.rating }}
28 | 29 | {{ my_paginator() }} 30 | 31 | {% endblock %} 32 | -------------------------------------------------------------------------------- /templates/paste/detail.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block page_header %}EOJ Pastebin{% endblock %} 4 | 5 | {% block content %} 6 | 17 | 18 |
19 | 25 |
{{ paste.code_as_html | safe }}
26 |
27 | {% endblock %} -------------------------------------------------------------------------------- /templates/account/profile.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'account/base.jinja2' %} 2 | {% from 'components/modal.jinja2' import modal %} 3 | 4 | 5 | {% block account_setting %} 6 | 7 |
8 |
9 | 10 |
11 | 12 | 13 |
14 |
15 |
16 | 17 | 18 | 19 | {% call modal("修改用户名", id="modify-username-modal", action=url('account:change_username')) %} 20 |
21 | {% csrf_token %} 22 |
23 | 24 | 25 |
26 |

这是你第 {{ user.username_change_attempt + 1 }} 次修改用户名的尝试,这将会消耗 {{ (user.username_change_attempt ** 2) * 100 }} EMB.

27 |
28 | {% endcall %} 29 | 30 | {% include 'components/form.jinja2' %} 31 | {% endblock %} -------------------------------------------------------------------------------- /utils/language.py: -------------------------------------------------------------------------------- 1 | from pygments import highlight 2 | from pygments.formatters.html import HtmlFormatter 3 | from pygments.lexers import get_lexer_by_name 4 | 5 | LANG_CHOICE = ( 6 | ('c', 'C'), 7 | ('cpp', 'C++11'), 8 | ('cc14', 'C++14'), 9 | ('cc17', 'C++17'), 10 | ('py2', 'Python 2'), 11 | ('python', 'Python 3'), 12 | ('pypy', 'PyPy'), 13 | ('pypy3', 'PyPy 3'), 14 | ('java', 'Java 8'), 15 | ('pas', 'Pascal'), 16 | ('text', 'Text') 17 | ) 18 | 19 | LANG_REGULAR_NAME = ( 20 | ('cc14', 'cpp'), 21 | ('cc17', 'cpp'), 22 | ('py2', 'python'), 23 | ('pypy3', 'python'), 24 | ('pas', 'pascal'), 25 | ) 26 | 27 | LANG_EXT = ( 28 | ('c', 'c'), 29 | ('cpp', 'cpp'), 30 | ('python', 'py'), 31 | ('java', 'java'), 32 | ('cc14', 'cpp'), 33 | ('cc17', 'cpp'), 34 | ('py2', 'py'), 35 | ('pypy', 'py'), 36 | ('pypy3', 'py'), 37 | ('pas', 'pas'), 38 | ('text', 'txt'), 39 | ) 40 | 41 | 42 | def transform_code_to_html(code, lang): 43 | if not lang: 44 | lang = "text" 45 | return highlight(code, get_lexer_by_name(dict(LANG_REGULAR_NAME).get(lang, lang)), HtmlFormatter()) 46 | -------------------------------------------------------------------------------- /pylintrc: -------------------------------------------------------------------------------- 1 | [SETTINGS] 2 | 3 | max-line-length=120 4 | max-args=8 5 | max-locals=15 6 | max-statements=50 7 | max-attributes=15 8 | indent-string=' ' 9 | const-naming-style=any 10 | indent-after-paren=2 11 | notes= 12 | 13 | disable=R, 14 | no-member, 15 | missing-module-docstring, 16 | missing-class-docstring, 17 | missing-method-docstring, 18 | missing-function-docstring, 19 | invalid-name, 20 | unused-argument, 21 | redefined-builtin, 22 | bare-except, 23 | broad-except, 24 | attribute-defined-outside-init, 25 | relative-beyond-top-level, 26 | import-outside-toplevel, 27 | invalid-str-returned, 28 | unused-variable, 29 | raise-missing-from, 30 | unspecified-encoding, 31 | consider-using-f-string, 32 | consider-using-dict-items, 33 | consider-iterating-dictionary, 34 | unsupported-binary-operation, 35 | broad-exception-raised, 36 | nested-min-max, 37 | unknown-option-value, 38 | missing-timeout 39 | 40 | ignore=migrations -------------------------------------------------------------------------------- /templates/contest/balloon_detail.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'html_base.jinja2' %} 2 | 3 | {% block css %} 4 | 19 | {% endblock %} 20 | 21 | {% block _content %} 22 |
23 | 24 |
25 |

26 | {{ submission.username }} 27 |

28 |

29 | Problem {{ submission.contest_problem.identifier }} 30 |

31 | 32 |
33 | {% csrf_token %} 34 | 35 | Done 36 | 37 | 38 |
39 |
40 |
41 | 42 | {% endblock %} 43 | -------------------------------------------------------------------------------- /static/js/input.file.js: -------------------------------------------------------------------------------- 1 | $.fn.inputFile = function(data) { 2 | var sizeLimit = -1; 3 | if (data !== undefined && data.hasOwnProperty("sizeLimit")) { 4 | sizeLimit = parseInt(data["sizeLimit"]); 5 | } 6 | function changeFunction(e) { 7 | var file = $(e.target); 8 | var name = ''; 9 | var size = 0; 10 | for (var i = 0; i < e.target.files.length; i++) { 11 | name += e.target.files[i].name + (i + 1 == e.target.files.length ? '' : ', '); 12 | size += e.target.files[i].size / 1048576; 13 | } 14 | $('input:text', file.parent()).val(name); 15 | if (sizeLimit > 0 && size > sizeLimit) { 16 | window.alert("File too large! If you insist on uploading, it is likely to fail..."); 17 | } 18 | } 19 | if (!this.prop("init")) { 20 | this.prop("init", true); 21 | this.find('input:text, .ui.button:not([type="submit"])') 22 | .on('click', function (e) { 23 | $(e.target).parent().find('input:file').click(); 24 | }) 25 | ; 26 | this.on('change', changeFunction); 27 | this.find('.ui.file.input').on('change', changeFunction); 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /templates/components/profile_card.jinja2: -------------------------------------------------------------------------------- 1 | {% macro profile_card(profile, solved) %} 2 | 3 |
4 |
5 | 6 |
7 |
8 | {{ profile.get_username_display() }}{% if profile.pk == request.user.pk %} 9 | {% endif %} 10 |
11 | {% with join_time = (profile.date_joined | naturaltime) %} 12 | 注册于 {{ join_time }} 13 | {% endwith %} 14 |
15 | {% if profile.school or profile.motto %} 16 |
17 | {% if profile.school %}目前正在 {{ profile.school }} 工作/学习{% endif %} 18 | {% if profile.motto %}
{{ profile.motto }}{% endif %} 19 |
20 | {% endif %} 21 |
22 | 25 |
26 | 27 | {% endmacro %} -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 EOJ Team 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 | -------------------------------------------------------------------------------- /backstage/blog/views.py: -------------------------------------------------------------------------------- 1 | from django.db import transaction 2 | from django.shortcuts import HttpResponse 3 | from django.views.generic.list import ListView, View 4 | 5 | from blog.models import Blog 6 | from ..base_views import BaseBackstageMixin 7 | 8 | 9 | class BlogList(BaseBackstageMixin, ListView): 10 | template_name = 'backstage/blog/blog.jinja2' 11 | paginate_by = 200 12 | context_object_name = 'blog_list' 13 | queryset = Blog.objects.select_related("author") 14 | 15 | 16 | class BlogVisibleSwitch(BaseBackstageMixin, View): 17 | def post(self, request, pk): 18 | with transaction.atomic(): 19 | instance = Blog.objects.select_for_update().get(pk=pk) 20 | instance.visible = not instance.visible 21 | instance.save(update_fields=["visible"]) 22 | return HttpResponse() 23 | 24 | 25 | class BlogRecommendSwitch(BaseBackstageMixin, View): 26 | def post(self, request, pk): 27 | with transaction.atomic(): 28 | instance = Blog.objects.select_for_update().get(pk=pk) 29 | instance.recommend = not instance.recommend 30 | instance.save(update_fields=["recommend"]) 31 | return HttpResponse() 32 | -------------------------------------------------------------------------------- /notification/views.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.mixins import LoginRequiredMixin 2 | from django.views.generic import ListView 3 | from rest_framework.views import APIView, status, Response 4 | 5 | 6 | class NotificationListView(LoginRequiredMixin, ListView): 7 | template_name = 'notification/list.jinja2' 8 | context_object_name = 'notifications' 9 | paginate_by = 20 10 | 11 | def get_queryset(self): 12 | return self.request.user.notifications.active() 13 | 14 | def get(self, request, *args, **kwargs): 15 | self.request.user.notifications.mark_all_as_read() 16 | return super().get(request, *args, **kwargs) 17 | 18 | 19 | class NotificationMarkAllAsRead(LoginRequiredMixin, APIView): 20 | 21 | def post(self, request): 22 | request.user.notifications.mark_all_as_read() 23 | return Response(status=status.HTTP_200_OK) 24 | 25 | 26 | class NotificationMarkAsRead(LoginRequiredMixin, APIView): 27 | 28 | def post(self, request, pk): 29 | try: 30 | request.user.notifications.get(pk=pk).mark_as_read() 31 | return Response(status=status.HTTP_200_OK) 32 | except: 33 | return Response(status=status.HTTP_400_BAD_REQUEST) 34 | -------------------------------------------------------------------------------- /polygon/problem2/utils.py: -------------------------------------------------------------------------------- 1 | import re 2 | from datetime import datetime 3 | from os import path, listdir 4 | 5 | from django.conf import settings 6 | 7 | 8 | def sort_out_directory(directory): 9 | if not path.exists(directory): 10 | return [] 11 | return sorted(list(map(lambda file: {'filename': path.basename(file), 12 | 'modified_time': datetime.fromtimestamp(path.getmtime(file)).\ 13 | strftime(settings.DATETIME_FORMAT_TEMPLATE), 14 | 'size': path.getsize(file)}, 15 | listdir_with_prefix(directory))), 16 | key=lambda d: d['modified_time'], reverse=True) 17 | 18 | 19 | def listdir_with_prefix(directory): 20 | return list(map(lambda file: path.join(directory, file), 21 | filter(lambda f2: not f2.startswith('.'), 22 | listdir(directory)))) 23 | 24 | 25 | def normal_regex_check(alias): 26 | return re.match(r"^[\.a-z0-9_-]{4,64}$", alias) 27 | 28 | 29 | def valid_fingerprint_check(fingerprint): 30 | return re.match(r"^[a-z0-9]{16,128}$", fingerprint) 31 | -------------------------------------------------------------------------------- /polygon/templates/polygon/problem2/case/create.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'polygon/problem2/base.jinja2' %} 2 | 3 | {% block problem_content %} 4 | {% include 'components/form.jinja2' %} 5 | {% endblock %} 6 | 7 | {% block local_script %} 8 | 37 | {% endblock %} -------------------------------------------------------------------------------- /problem/testlib/generator/iwgen.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Outputs weighted random number between 1 and 10^6, inclusive. 3 | * To generate different values, call "nwgen.exe weight". 4 | * 5 | * If parameter "weight" 6 | * is equals to 0 than used uniformly distributed random. 7 | * 8 | * If parameter "weight" > 0 then you can think about it as code like this: 9 | * 10 | * result = rnd.next(1, 1000000); 11 | * for (int i = 0; i < weight; i++) 12 | * result = max(result, rnd.next(1, 1000000)); 13 | * 14 | * 15 | * If parameter "weight" < 0 then you can think about it as code like this: 16 | * 17 | * result = rnd.next(1, 1000000); 18 | * for (int i = 0; i < -weight; i++) 19 | * result = min(result, rnd.next(1, 1000000)); 20 | * 21 | * 22 | * It is typical behaviour of "wnext" methods to use this strategy to 23 | * generate off-center random distribution. 24 | */ 25 | 26 | #include "testlib.h" 27 | #include 28 | 29 | using namespace std; 30 | 31 | int main(int argc, char* argv[]) 32 | { 33 | registerGen(argc, argv, 1); 34 | 35 | cout << rnd.wnext(1, 1000000, atoi(argv[1])) << endl; 36 | 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /static/less/layout.less: -------------------------------------------------------------------------------- 1 | #navbar-small { 2 | @media only screen and (min-width: 768px) { 3 | display: none; 4 | } 5 | } 6 | #navbar-large { 7 | @media only screen and (max-width: 767px) { 8 | display: none; 9 | } 10 | } 11 | 12 | .main.container { 13 | margin-top: 7em; 14 | } 15 | .wireframe { 16 | margin-top: 2em; 17 | } 18 | .ui.footer.segment { 19 | margin: 5em 0 0; 20 | padding: 5em 0; 21 | } 22 | .ui.pagination.menu.secondary { 23 | a.item, div.item { 24 | min-width: 0; 25 | padding: .8em; 26 | > i.icon { 27 | margin: 0; 28 | } 29 | } 30 | } 31 | 32 | .local-body { 33 | display: flex; 34 | min-height: 100vh; 35 | flex-direction: column; 36 | .footer { 37 | flex: 1; 38 | } 39 | } 40 | 41 | .ui.accordion.list .item .title { 42 | padding: 0; 43 | } 44 | 45 | #past-submissions { 46 | margin-top: 3em; 47 | } 48 | 49 | #sub-heatmap { 50 | @media only screen and (max-width: 767px) { 51 | display: none; 52 | } 53 | } 54 | 55 | .hidden-on-small { 56 | @media only screen and (max-width: 767px) { 57 | display: none; 58 | } 59 | } 60 | 61 | .notifications .ui.message:last-child { 62 | margin-bottom: 2em; 63 | } -------------------------------------------------------------------------------- /templates/backstage/site/site.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'backstage/base.jinja2' %} 2 | 3 | {% block content_header %} 4 | Site settings 5 | {% endblock %} 6 | 7 | {% block backstage_content %} 8 | 9 |
10 | Use the same key to override. Submit an empty message to delete. 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | {% for settings in site_settings %} 21 | 22 | 23 | 24 | 25 | {% endfor %} 26 | 27 |
KeyValue
{{ settings.key }}{{ settings.val }}
28 | 29 | 30 |
31 | {% csrf_token %} 32 | 33 |
34 | 35 | 36 |
37 |
38 | 39 | 40 |
41 | 42 |
43 | {% endblock %} 44 | -------------------------------------------------------------------------------- /utils/markdown3/mdx_downheader.py: -------------------------------------------------------------------------------- 1 | from markdown import Extension 2 | from markdown.treeprocessors import Treeprocessor 3 | 4 | 5 | class DownHeaderTreeProcessor(Treeprocessor): 6 | def __init__(self, levels): 7 | self.levels = levels 8 | super(DownHeaderTreeProcessor, self).__init__() 9 | 10 | def run(self, root): 11 | for elem in root: 12 | if elem.tag in ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']: 13 | original_level = int(elem.tag[-1]) 14 | level = original_level + int(self.levels) 15 | level = min(6, max(1, level)) 16 | elem.tag = 'h' + str(level) 17 | 18 | 19 | class DownHeaderExtension(Extension): 20 | def __init__(self, *args, **kwargs): 21 | self.config = { 22 | 'levels': [1, 'downgrade headings this many levels'], 23 | } 24 | super(DownHeaderExtension, self).__init__(**kwargs) 25 | 26 | def extendMarkdown(self, md): 27 | if 'downheader' not in md.treeprocessors: 28 | treeprocessor = DownHeaderTreeProcessor(self.getConfig('levels')) 29 | md.treeprocessors.add('downheader', treeprocessor, '_end') 30 | 31 | 32 | def makeExtension(*args, **kwargs): 33 | return DownHeaderExtension(*args, **kwargs) 34 | -------------------------------------------------------------------------------- /templates/print/admin.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block title %}Print Admin - {% endblock %} 4 | 5 | {% block page_header %}Print Admin{% endblock %} 6 | 7 | {% block content %} 8 | 9 |
10 | {% csrf_token %} 11 |
12 | 13 | 14 |
15 |
16 | 17 | 18 |
19 |
20 | 21 |
22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | {% for manager in print_manager_list %} 34 | 35 | 36 | 37 | 38 | 39 | {% endfor %} 40 | 41 |
UserLimitCreated
{{ manager.user.username }}{{ manager.limit }}{{ manager.create_time | date('Y-m-d H:i:s') }}
42 | 43 | {% endblock %} 44 | -------------------------------------------------------------------------------- /polygon/templates/polygon/problem2/program/preview.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'polygon/problem2/base.jinja2' %} 2 | 3 | {% block problem_content %} 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 |
Name{{ program.name }}
Language{{ program.get_lang_display() }}
Tag{{ program.get_tag_display() }}
Created{{ program.create_time | date("Y-m-d H:i:s") }}
Updated{{ program.update_time | date("Y-m-d H:i:s") }}
Fingerprint{{ program.fingerprint }}
Based on{% if program.parent_id %}{{ program.parent_id }}{% endif %}
Code{{ program.code_as_html | safe }}
39 | {% endblock %} -------------------------------------------------------------------------------- /submission/assets/template.tex: -------------------------------------------------------------------------------- 1 | % !TEX encoding = UTF-8 Unicode 2 | \RequirePackage{fix-cm} 3 | % \documentclass[a4paper,11pt,UTF8]{article} 4 | \documentclass[a4paper,10pt,UTF8]{ctexart} 5 | 6 | \usepackage{fancyhdr,array,lastpage,amsmath,mathtools,enumitem,graphicx,multirow,tocbibind,longtable,makecell,varwidth,titlesec,bm,booktabs,comment,pdfpages} 7 | %\setCJKmainfont[BoldFont=Heiti SC Medium]{Songti SC Light} 8 | %\setCJKsansfont{Heiti SC} 9 | 10 | \usepackage[left=2cm,right=2cm,top=3cm,bottom=2.5cm]{geometry} 11 | \usepackage[font=footnotesize,labelfont=bf]{caption} 12 | \usepackage{ctex} 13 | \usepackage{listings} 14 | \usepackage[Q=yes]{examplep} 15 | \setmonofont{Courier} 16 | 17 | \pagestyle{fancy} 18 | \lhead{$$username$$} 19 | \rhead{$$comment$$} 20 | \renewcommand{\headrulewidth}{0.4pt} 21 | 22 | \lstset{escapeinside=``, breaklines=true, frame=none, extendedchars=false, basicstyle=\ttfamily\small, showstringspaces=false, tabsize=4, numbers=left, stepnumber=1} 23 | 24 | \setlength{\parindent}{2em} 25 | \setlength{\parskip}{1.5ex plus 0.5ex minus 0.2ex} 26 | \linespread{1.0} 27 | 28 | \begin{document} 29 | 30 | \begin{lstlisting}[language=C++] 31 | $$code$$ 32 | \end{lstlisting} 33 | 34 | \end{document} 35 | -------------------------------------------------------------------------------- /templates/components/blog_preview.jinja2: -------------------------------------------------------------------------------- 1 | {% macro blog_preview(blog) %} 2 | {# overflow hidden (temporary) #} 3 |
4 |
5 | 6 |
7 |
8 |
9 | {{ username_display(blog.author) }}: {{ blog.title }} 10 |
11 | {{ blog.edit_time | naturaltime }} 12 |
13 |

14 |
15 |
16 | {% cache 3600 'blog_preview#'+blog.pk.__str__()+'#'+blog.edit_time.__str__() %} 17 | {{ blog.text | markdown | get_intro }} 18 | {% endcache %} 19 | ...查看全文 20 |
21 | 27 |
28 |
29 | {% endmacro %} -------------------------------------------------------------------------------- /templates/contest/activity/list.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block title %}Activities - {% endblock %} 4 | 5 | {% block page_header %}Activities{% endblock %} 6 | 7 | {% block content %} 8 | 9 | {% if privileged %} 10 | Add an activity 11 | {% endif %} 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | {# #} 21 | 22 | 23 | 24 | {% for activity in activity_list %} 25 | 26 | 27 | 28 | 29 | 30 | {# #} 31 | 32 | {% endfor %} 33 | 34 |
#NameRegister OpenRegister ClosedParticipants
{{ activity.pk }}{{ activity.title }}{{ activity.register_start_time | date("Y-m-d H:i") }}{{ activity.register_end_time | date("Y-m-d H:i") }} × {{ activity.participants__count }}
35 | 36 | {% endblock %} 37 | -------------------------------------------------------------------------------- /docs/Development.md: -------------------------------------------------------------------------------- 1 | # Development 2 | 3 | This tutorial will help you get started with development of EOJ. 4 | 5 | ## Installation 6 | 7 | 1. `pip install -r requirements.txt`. Try to use a virtualenv (avoid conda or global env). 8 | 2. Install and build static files: `cd static && yarn install && yarn build`. 9 | 3. Add your `local_settings.py`. A typical `local_settings.py` would be: 10 | 11 | ```python 12 | import os 13 | 14 | DEBUG = True 15 | PROJECT_PATH = os.path.abspath(os.path.dirname(os.path.dirname(__file__))) 16 | 17 | DATABASES = { 18 | 'default': { 19 | 'ENGINE': 'django.db.backends.sqlite3', 20 | 'NAME': os.path.join(PROJECT_PATH, 'database.sqlite'), 21 | } 22 | } 23 | ``` 24 | 25 | 4. Install a Redis server and get it running at 6379. 26 | 5. Initialize database: `python manage.py migrate`. 27 | 6. Create your first account with `python manage.py createsuperuser`. 28 | 7. Launch your development server with `python manage.py runserver 8080`. 29 | 30 | ## Troublshooting 31 | 32 | 1. Failed to install `mysqlclient`. This dependency is optional. Temporarily disable `mysqlclient` if installing it is troublesome and you are not using MySQL anyway. 33 | 2. Failed to install pycrypto on macOS. The following ticket might be helpful: https://github.com/trailofbits/algo/issues/516. 34 | -------------------------------------------------------------------------------- /polygon/templates/polygon/contest/base.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'polygon/polygon_base.jinja2' %} 2 | {% from 'components/search_user.jinja2' import user_search_multiple %} 3 | 4 | {% block title %}Contest Manage - {% endblock %} 5 | 6 | {% block content %} 7 |

Contest: {{ contest.title }}

8 | 9 | 18 | 19 | {% block contest_content %} 20 | {% endblock %} 21 | 22 | {% endblock %} -------------------------------------------------------------------------------- /templates/contest/problem.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'contest/base.jinja2' %} 2 | {% from 'components/problem.jinja2' import problem_view with context %} 3 | {% from 'components/submit.jinja2' import submit_view with context %} 4 | {% block contest_title %}{{ contest_problem.identifier }}. {{ problem.title }} - {% endblock %} 5 | {% block contest_content %} 6 |
{{ contest_problem.identifier }}. {{ problem.title }}
7 | 8 | {% include 'contest/contest_pdf_statement_notice.jinja2' %} 9 | 10 | {{ problem_view(problem, contest) }} 11 | 12 | {% if request.user.is_authenticated %} 13 | 14 | {# {% if attempt_left > 0 %}#} 15 | {#
{{ attempt_left }} attempts left.
#} 16 | {# {% else %}#} 17 | {#
Running out of attempts.
#} 18 | {# {% endif %}#} 19 | {{ submit_view(submit_action=url('contest:submit', contest.pk, contest_problem.identifier), margin_top=True) }} 20 | {% endif %} 21 | 22 | 29 | 30 | {% endblock %} 31 | -------------------------------------------------------------------------------- /utils/upload.py: -------------------------------------------------------------------------------- 1 | from os import path, makedirs 2 | 3 | 4 | def save_uploaded_file_to(file, directory, filename=None, size_limit=None, keep_extension=False): 5 | """ 6 | :param file: UploadedFile instance 7 | :param directory: the real dirname of your destination 8 | :param filename: file name by default 9 | :param size_limit: by megabytes, exceeding size_limit will raise ValueError 10 | :param keep_extension: True or False, if you rename your file and filename does not have an extension, the 11 | original filename's extension will be used as the new extension. If the original file 12 | does not have an extension, then nothing will happen. 13 | :return: new path 14 | """ 15 | if size_limit and file.size > size_limit * 1024576: 16 | raise ValueError("File is too large") 17 | if keep_extension and filename: 18 | raw_ext = path.splitext(file.name)[1] 19 | _, filename_ext = path.splitext(filename) 20 | if filename_ext != raw_ext: 21 | filename = filename + raw_ext 22 | makedirs(directory, exist_ok=True) 23 | new_path = path.join(directory, filename if filename else file.name) 24 | with open(new_path, 'wb+') as destination: 25 | for chunk in file.chunks(): 26 | destination.write(chunk) 27 | return new_path 28 | -------------------------------------------------------------------------------- /paste/models.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime, timedelta 2 | 3 | from django.db import models 4 | 5 | from account.models import User 6 | from utils.language import LANG_CHOICE 7 | 8 | 9 | class Paste(models.Model): 10 | fingerprint = models.CharField("指纹", unique=True, max_length=64) 11 | code = models.TextField("代码", blank=False) 12 | lang = models.CharField("语言", default='cpp', max_length=12, choices=LANG_CHOICE) 13 | created_by = models.ForeignKey(User, null=True, related_name="created_pastes", on_delete=models.SET_NULL) 14 | public_access = models.PositiveIntegerField("访问权限", choices=( 15 | (0, '只有自己可见'), 16 | (10, '对受邀用户可见'), 17 | (20, '对所有人可见') 18 | ), default=20) 19 | create_time = models.DateTimeField(auto_now_add=True) 20 | invited_users = models.ManyToManyField(User, verbose_name="受邀用户", related_name="invited_pastes") 21 | is_deleted = models.BooleanField(default=False) 22 | expire_after = models.IntegerField(verbose_name="过期", choices=( 23 | (-1, '永不'), 24 | (1, '1 分钟'), 25 | (10, '10 分钟'), 26 | (60, '1 小时'), 27 | (300, '5 小时'), 28 | (1440, '1 天'), 29 | (43200, '30 天'), 30 | ), default=-1) 31 | 32 | @property 33 | def expired(self): 34 | return self.expire_after > 0 and datetime.now() > timedelta(minutes=self.expire_after) + self.create_time 35 | -------------------------------------------------------------------------------- /problem/testlib/pack.py: -------------------------------------------------------------------------------- 1 | """ 2 | This is used to pack testlib into a json 3 | Then you can load into database by using: 4 | manage.py loaddata 5 | fixturename here is `testlib.json` 6 | """ 7 | 8 | import hashlib 9 | import json 10 | from os import path, listdir 11 | 12 | 13 | def hash(binary): 14 | return hashlib.sha256(binary).hexdigest() 15 | 16 | 17 | category = ['checker', 'generator', 'validator', 'validator'] 18 | father_dir = path.dirname(__file__) 19 | output_file = open(path.join(father_dir, 'testlib.json'), 'w') 20 | data = [] 21 | for cat in category: 22 | for file in listdir(path.join(father_dir, cat)): 23 | if file.startswith('.'): 24 | continue 25 | with open(path.join(father_dir, cat, file)) as fs: 26 | code = fs.read() 27 | with open(path.join(father_dir, cat, file), 'rb') as fs: 28 | code_binary = fs.read() 29 | data.append(dict(model='problem.SpecialProgram', 30 | fields=dict( 31 | fingerprint=hash(code_binary), 32 | filename=file, 33 | code=code, 34 | lang='cpp', 35 | category=cat, 36 | builtin=True 37 | ))) 38 | 39 | json.dump(data, output_file) 40 | output_file.close() 41 | -------------------------------------------------------------------------------- /utils/permission.py: -------------------------------------------------------------------------------- 1 | from django.db.models import Q 2 | 3 | from account.permissions import is_admin_or_root 4 | 5 | 6 | def is_problem_manager(user, problem): 7 | return user.is_authenticated and (is_admin_or_root(user) or problem.managers.filter(pk=user.pk).exists()) 8 | 9 | 10 | def is_contest_manager(user, contest): 11 | return user.is_authenticated and (is_admin_or_root(user) or contest.managers.filter(pk=user.pk).exists()) 12 | 13 | 14 | def is_contest_volunteer(user, contest): 15 | return user.is_authenticated and (contest.volunteers.filter(pk=user.pk).exists() 16 | or is_contest_manager(user, contest)) 17 | 18 | 19 | def get_permission_for_submission(user, submission, special_permission=False): 20 | if user.is_authenticated: 21 | if submission.contest and is_contest_manager(user, submission.contest): 22 | return 2 23 | if submission.problem and is_problem_manager(user, submission.problem): 24 | return 2 25 | if user == submission.author or special_permission: 26 | return 1 27 | return 0 28 | 29 | 30 | def is_case_download_available(user, problem_id, contest_id=None): 31 | q = Q(problem_id=problem_id) 32 | if contest_id: 33 | q &= Q(contest_id=contest_id) 34 | return user.is_authenticated and user.submission_set.filter(q).count() > 5 35 | -------------------------------------------------------------------------------- /problem/testlib/checker/lcmp.cpp: -------------------------------------------------------------------------------- 1 | #include "testlib.h" 2 | #include 3 | #include 4 | #include 5 | 6 | using namespace std; 7 | 8 | bool compareWords(string a, string b) 9 | { 10 | vector va, vb; 11 | stringstream sa; 12 | 13 | sa << a; 14 | string cur; 15 | while (sa >> cur) 16 | va.push_back(cur); 17 | 18 | stringstream sb; 19 | sb << b; 20 | while (sb >> cur) 21 | vb.push_back(cur); 22 | 23 | return (va == vb); 24 | } 25 | 26 | int main(int argc, char * argv[]) 27 | { 28 | setName("compare files as sequence of tokens in lines"); 29 | registerTestlibCmd(argc, argv); 30 | 31 | std::string strAnswer; 32 | 33 | int n = 0; 34 | while (!ans.eof()) 35 | { 36 | std::string j = ans.readString(); 37 | 38 | if (j == "" && ans.eof()) 39 | break; 40 | 41 | std::string p = ouf.readString(); 42 | strAnswer = p; 43 | 44 | n++; 45 | 46 | if (!compareWords(j, p)) 47 | quitf(_wa, "%d%s lines differ - expected: '%s', found: '%s'", n, englishEnding(n).c_str(), compress(j).c_str(), compress(p).c_str()); 48 | } 49 | 50 | if (n == 1) 51 | quitf(_ok, "single line: '%s'", compress(strAnswer).c_str()); 52 | 53 | quitf(_ok, "%d lines", n); 54 | } 55 | -------------------------------------------------------------------------------- /static/js/countdown.js: -------------------------------------------------------------------------------- 1 | $(".ui.progress").each(function () { 2 | if ($(this).data("status")) { 3 | var status = $(this).data("status"); 4 | if (status > 0) $(this).progress({ percent: 100 }); 5 | else $(this).progress({ percent: 0 }); 6 | } 7 | }); 8 | 9 | var timer = setInterval(function() { 10 | function fixedTwo(number) { 11 | if (number == 0) return "00"; 12 | return number < 10 ? ("0" + number) : ("" + number); 13 | } 14 | 15 | var countdowns = $(".countdown"); 16 | countdowns.each(function () { 17 | var seconds = Math.floor($(this).data("delta-seconds")) - 1; 18 | $(this).data("delta-seconds", seconds); 19 | var show_time = Math.floor(seconds / 3600) + ":" + fixedTwo(Math.floor((seconds % 3600) / 60)) + ":" + fixedTwo(seconds % 60); 20 | $(this).html(show_time); 21 | if (seconds <= 0) { 22 | clearInterval(timer); 23 | $('#refreshNotification').modal('show'); 24 | } 25 | 26 | var progress = $(this).closest(".ui.progress"); 27 | if (progress.length > 0) { 28 | var now_progress = 0; 29 | if ($(this).data("duration") > 0) { 30 | now_progress = 100 - $(this).data('delta-seconds') / $(this).data('duration') * 100; 31 | progress.progress({ 32 | percent: now_progress 33 | }); 34 | } 35 | } 36 | }); 37 | 38 | }, 1000); 39 | -------------------------------------------------------------------------------- /templates/notification/list.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block title %}Notifications - {% endblock %} 4 | 5 | {% block page_header %}Notifications{% endblock %} 6 | 7 | {% block content %} 8 | 9 | {% for item in notifications %} 10 | 11 |
12 |
13 |
14 | 15 |
16 |
17 |
18 | {{ item.timestamp | naturaltime }} 19 |
20 |
21 | {{ username_display(item.actor) }} {{ item.verb }} 22 | {% if item.target.__class__.__name__ == 'Contest' %} 23 | {{ item.target.title }} 24 | {% elif item.target.__class__.__name__ == 'Problem' %} 25 | {{ item.target.title }} 26 | {% elif item.target.__class__.__name__ == 'Blog' %} 27 | {{ item.target.title }} 28 | {% endif %} 29 |
30 |
31 |
32 |
33 | 34 | {% endfor %} 35 | 36 | {{ my_paginator() }} 37 | 38 | {% endblock %} 39 | -------------------------------------------------------------------------------- /problem/testlib/generator/gen-rooted-tree-graph.cpp: -------------------------------------------------------------------------------- 1 | #include "testlib.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #define forn(i, n) for (int i = 0; i < int(n); i++) 26 | 27 | using namespace std; 28 | 29 | int main(int argc, char* argv[]) 30 | { 31 | registerGen(argc, argv, 1); 32 | 33 | int n = atoi(argv[1]); 34 | int t = atoi(argv[2]); 35 | 36 | vector p(n); 37 | forn(i, n) 38 | if (i > 0) 39 | p[i] = rnd.wnext(i, t); 40 | 41 | printf("%d\n", n); 42 | vector perm(n); 43 | forn(i, n) 44 | perm[i] = i; 45 | shuffle(perm.begin() + 1, perm.end()); 46 | 47 | vector pp(n); 48 | for (int i = 1; i < n; i++) 49 | pp[perm[i]] = perm[p[i]]; 50 | 51 | for (int i = 1; i < n; i++) 52 | { 53 | printf("%d", pp[i] + 1); 54 | if (i + 1 < n) 55 | printf(" "); 56 | } 57 | printf("\n"); 58 | 59 | return 0; 60 | } 61 | -------------------------------------------------------------------------------- /templates/backstage/email/list.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'backstage/base.jinja2' %} 2 | {% from 'components/post_link.jinja2' import post_link %} 3 | {% block content_header %} 4 | Emails 5 | {% endblock %} 6 | 7 | {% block backstage_content %} 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | {% for email in email_list %} 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | {% endfor %} 38 | 39 |
13 | 14 | 添加邮件 15 | 16 |
#标题内容创建更新作者
{{ email.pk }}{{ email.title }}{{ email.content | length }}{{ email.create_time | date('Y-m-d H:i:s') }}{{ email.update_time | date('Y-m-d H:i:s') }}{% if email.created_by %}{{ username_display(email.created_by) }}{% endif %}
40 | 41 | {% endblock %} 42 | -------------------------------------------------------------------------------- /problem/testlib/validator/bipartite-graph-validator.cpp: -------------------------------------------------------------------------------- 1 | #include "testlib.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #define forn(i, n) for (int i = 0; i < int(n); i++) 26 | 27 | using namespace std; 28 | 29 | int main(int argc, char* argv[]) 30 | { 31 | registerValidation(argc, argv); 32 | 33 | int n = inf.readInt(1, 400, "n"); 34 | inf.readSpace(); 35 | int m = inf.readInt(1, 400, "m"); 36 | inf.readSpace(); 37 | int k = inf.readInt(0, n * m, "k"); 38 | inf.readEoln(); 39 | 40 | set > edges; 41 | 42 | forn(i, k) 43 | { 44 | int a = inf.readInt(1, n, "a_i"); 45 | inf.readSpace(); 46 | int b = inf.readInt(1, m, "b_i"); 47 | inf.readEoln(); 48 | 49 | ensuref(edges.count(make_pair(a, b)) == 0, "Graph can't contain multiple edges between a pair of vertices"); 50 | edges.insert(make_pair(a, b)); 51 | } 52 | 53 | inf.readEof(); 54 | return 0; 55 | } 56 | -------------------------------------------------------------------------------- /submission/util.py: -------------------------------------------------------------------------------- 1 | class SubmissionStatus(object): 2 | SUBMITTED = -4 3 | WAITING = -3 4 | JUDGING = -2 5 | WRONG_ANSWER = -1 6 | ACCEPTED = 0 7 | TIME_LIMIT_EXCEEDED = 1 8 | IDLENESS_LIMIT_EXCEEDED = 2 9 | MEMORY_LIMIT_EXCEEDED = 3 10 | RUNTIME_ERROR = 4 11 | SYSTEM_ERROR = 5 12 | COMPILE_ERROR = 6 13 | SCORED = 7 14 | REJECTED = 10 15 | JUDGE_ERROR = 11 16 | PRETEST_PASSED = 12 17 | 18 | @staticmethod 19 | def is_judged(status): 20 | return status >= SubmissionStatus.WRONG_ANSWER 21 | 22 | @staticmethod 23 | def is_penalty(status): 24 | return SubmissionStatus.is_judged(status) and status != SubmissionStatus.COMPILE_ERROR 25 | 26 | @staticmethod 27 | def is_accepted(status): 28 | return status == SubmissionStatus.ACCEPTED or status == SubmissionStatus.PRETEST_PASSED 29 | 30 | @staticmethod 31 | def is_scored(status): 32 | return status == SubmissionStatus.SCORED 33 | 34 | 35 | STATUS_CHOICE = ( 36 | (-4, 'Submitted'), 37 | (-3, 'In queue'), 38 | (-2, 'Running'), 39 | (-1, 'Wrong answer'), 40 | (0, 'Accepted'), 41 | (1, 'Time limit exceeded'), 42 | (2, 'Idleness limit exceeded'), 43 | (3, 'Memory limit exceeded'), 44 | (4, 'Runtime error'), 45 | (5, 'Denial of judgement'), 46 | (6, 'Compilation error'), 47 | (7, 'Partial score'), 48 | (10, 'Rejected'), 49 | (11, 'Checker error'), 50 | (12, 'Pretest passed'), 51 | ) 52 | -------------------------------------------------------------------------------- /templates/backstage/contest/contest.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'backstage/base.jinja2' %} 2 | {% from 'components/post_link.jinja2' import post_link %} 3 | {% block content_header %} 4 | 比赛 5 | {% endblock %} 6 | 7 | {% block backstage_content %} 8 | 9 | 重新计算用户颜色 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | {% for contest in contest_list %} 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | {# NOTE: Contest cannot be deleted once created #} 31 | 32 | {% endfor %} 33 | 34 |
#标题开始时间结束时间积分管理
{{ contest.pk }}{{ contest.title }}{{ contest.start_time | date('Y-m-d H:i') }}{{ contest.end_time | date('Y-m-d H:i') }}重算撤回
35 | 36 | {{ my_paginator() }} 37 | 38 | {% endblock %} 39 | -------------------------------------------------------------------------------- /account/permissions.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth import get_user_model 2 | 3 | from django.contrib.auth.backends import ModelBackend 4 | from django.contrib.auth.mixins import AccessMixin 5 | 6 | 7 | def is_admin_or_root(user): 8 | return user.is_authenticated and (user.is_staff or user.is_superuser) 9 | 10 | 11 | def is_coach(user): 12 | return user.is_authenticated and user.magic == "red" 13 | 14 | 15 | class UsernameOrEmailModelBackend(ModelBackend): 16 | def authenticate(self, request, username=None, password=None, **kwargs): 17 | UserModel = get_user_model() 18 | if username is None: 19 | username = kwargs.get(UserModel.USERNAME_FIELD) 20 | try: 21 | if '@' not in username: 22 | user = UserModel.objects.get(username=username) 23 | else: 24 | user = UserModel.objects.get(email=username) 25 | except UserModel.DoesNotExist: 26 | UserModel().set_password(password) 27 | else: 28 | if user.check_password(password) and self.user_can_authenticate(user): 29 | return user 30 | 31 | 32 | class StaffRequiredMixin(AccessMixin): 33 | """ 34 | CBV mixin which verifies that the current user is staff. 35 | """ 36 | 37 | def dispatch(self, request, *args, **kwargs): 38 | if not is_admin_or_root(request.user): 39 | return self.handle_no_permission() 40 | return super(StaffRequiredMixin, self).dispatch(request, *args, **kwargs) 41 | -------------------------------------------------------------------------------- /templates/components/footer.jinja2: -------------------------------------------------------------------------------- 1 | 28 | -------------------------------------------------------------------------------- /templates/components/pagination.jinja2: -------------------------------------------------------------------------------- 1 |
2 | 33 |
34 | -------------------------------------------------------------------------------- /dispatcher/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | from problem.models import Problem 4 | 5 | 6 | class Server(models.Model): 7 | name = models.CharField("名字", max_length=30, unique=True) 8 | ip = models.CharField("IP", max_length=30) 9 | port = models.IntegerField("端口号") 10 | token = models.CharField("密钥", max_length=192) 11 | add_time = models.DateTimeField("创建时间", auto_now_add=True) 12 | last_seen_time = models.DateTimeField(auto_now=True) 13 | last_synchronize_time = models.DateTimeField(null=True) 14 | enabled = models.BooleanField(default=False) 15 | concurrency = models.PositiveIntegerField("并发量", default=1) 16 | runtime_multiplier = models.FloatField("运行时间调整系数", default=1) 17 | version = models.PositiveIntegerField("判题机版本") 18 | master = models.BooleanField("主节点", default=True) 19 | 20 | def __str__(self): 21 | return self.name + ' - ' + self.ip 22 | 23 | class Meta: 24 | ordering = ["last_seen_time"] 25 | 26 | @property 27 | def http_address(self): 28 | return 'http://' + self.ip + ':' + str(self.port) 29 | 30 | 31 | class ServerProblemStatus(models.Model): 32 | server = models.ForeignKey(Server, on_delete=models.CASCADE) 33 | problem = models.ForeignKey(Problem, on_delete=models.CASCADE) 34 | last_status = models.TextField(blank=True) 35 | last_synchronize = models.DateTimeField(auto_now=True) 36 | 37 | class Meta: 38 | unique_together = ('server', 'problem') 39 | -------------------------------------------------------------------------------- /templates/backstage/base.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | {% from 'components/post_link.jinja2' import post_link %} 3 | {% from 'components/modal.jinja2' import modal with context %} 4 | {% block title %}Backstage - {% endblock %} 5 | 6 | {% block page_header %} 7 | Backstage: {% block content_header %}{% endblock %} 8 | {% endblock %} 9 | 10 | {% block content %} 11 | 12 | 21 | {% include 'components/message.jinja2' %} 22 | 23 | {% block backstage_content %} 24 | {% endblock %} 25 | 26 | {% include 'components/delete_confirmation.jinja2' %} 27 | 28 | {% endblock %} 29 | 30 | 31 | {% block script %} 32 | 33 | {% endblock %} -------------------------------------------------------------------------------- /problem/testlib/validator/undirected-graph-validator.cpp: -------------------------------------------------------------------------------- 1 | #include "testlib.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #define forn(i, n) for (int i = 0; i < int(n); i++) 26 | 27 | using namespace std; 28 | 29 | int main(int argc, char* argv[]) 30 | { 31 | registerValidation(argc, argv); 32 | 33 | int n = inf.readInt(1, 1000, "n"); 34 | inf.readSpace(); 35 | int m = inf.readInt(0, 100000, "m"); 36 | inf.readEoln(); 37 | 38 | set > edges; 39 | 40 | forn(i, m) 41 | { 42 | int a = inf.readInt(1, n, "a_i"); 43 | inf.readSpace(); 44 | int b = inf.readInt(1, n, "b_i"); 45 | inf.readEoln(); 46 | 47 | ensuref(a != b, "Graph can't contain loops"); 48 | ensuref(edges.count(make_pair(a, b)) == 0, "Graph can't contain multiple edges between a pair of vertices"); 49 | 50 | edges.insert(make_pair(a, b)); 51 | edges.insert(make_pair(b, a)); 52 | } 53 | 54 | inf.readEof(); 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /templates/submission.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | 3 | {% block title %}提交 #{{ submission.id }} - {% endblock %} 4 | 5 | {% block page_header %}提交 #{{ submission.id }}{% endblock %} 6 | 7 | {% block content %} 8 | 9 | {{ submission_block }} 10 | 11 |
12 | 23 |
{{ submission.code_as_html | safe }}
24 |
25 | 26 | {{ report_block }} 27 | 28 | {% if submission.author == request.user %} 29 | {% if submission.contest_problem and contest %} 30 |
31 | {% else %} 32 |
33 | {% endif %} 34 | {% endif %} 35 | 36 | {% endblock %} 37 | -------------------------------------------------------------------------------- /templates/problem/archive.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'problem/base.jinja2' %} 2 | 3 | {% macro render_problem(problem, comment='') %} 4 |
5 | {% if problem.personal_label == 1 %} 6 |
Solved
7 | {% elif problem.personal_label == -1 %} 8 |
Tried
9 | {% else %} 10 |
New
11 | {% endif %} 12 | {{ problem }} 13 |
14 | {% endmacro %} 15 | 16 | {% macro render_node(id) %} 17 | {% if children_list[id] or problem_list[id] %} 18 |
19 | {{ skill_list[id].name }} 20 |
21 | {% if children_list[id] %} 22 | {% for child in children_list[id] %} 23 | {{ render_node(child) }} 24 | {% endfor %} 25 | {% endif %} 26 | {% if problem_list[id] %} 27 | {% for p in (problem_list[id] | sort) %} 28 | {{ render_problem(problem_set[p]) }} 29 | {% endfor %} 30 | {% endif %} 31 |
32 |
33 | {% endif %} 34 | {% endmacro %} 35 | 36 | {% block title %}Archive - {% endblock %} 37 | 38 | {% block problem_content %} 39 |
Recommended Problems
40 |
41 | {% for node in children_list[-1] %} 42 | {{ render_node(node) }} 43 | {% endfor %} 44 |
45 | {% endblock %} -------------------------------------------------------------------------------- /utils/rsa_gen.py: -------------------------------------------------------------------------------- 1 | import base64 2 | 3 | from Crypto.Cipher import PKCS1_OAEP 4 | from Crypto.Hash import SHA256 5 | from Crypto.PublicKey import RSA 6 | from django.core.cache import cache 7 | 8 | 9 | def generate_RSA(bits=1024): 10 | new_key = RSA.generate(bits, e=65537) 11 | public_key = new_key.publickey().exportKey("PEM") 12 | private_key = new_key.exportKey("PEM") 13 | return private_key, public_key 14 | 15 | 16 | def doRSAFromBytes(key, plaintext): 17 | # Assuming that the public key is coming from java or javascript, 18 | # strip off the headers. 19 | key = key.replace('-----BEGIN PUBLIC KEY-----', '') 20 | key = key.replace('-----END PUBLIC KEY-----', '') 21 | # Since it's coming from java/javascript, it's base 64 encoded. 22 | # Decode before importing. 23 | pubkey = RSA.importKey(base64.b64decode(key)) 24 | cipher = PKCS1_OAEP.new(pubkey, hashAlgo=SHA256) 25 | encrypted = cipher.encrypt(plaintext) 26 | return base64.b64encode(encrypted) 27 | 28 | 29 | def decryptRSA(ciphertext, private_key): 30 | rsa_key = RSA.importKey(private_key) 31 | cipher = PKCS1_OAEP.new(rsa_key, hashAlgo=SHA256) 32 | decrypted = cipher.decrypt(base64.b64decode(ciphertext)) 33 | return decrypted 34 | 35 | 36 | def get_keys(): 37 | keys = cache.get("RSA_KEYS") 38 | if keys is None: 39 | keys = generate_RSA() 40 | cache.set("RSA_KEYS", keys, 3600) 41 | return keys 42 | 43 | 44 | def get_public_key(): 45 | _, pub = get_keys() 46 | return pub.decode() 47 | -------------------------------------------------------------------------------- /api/views/submission.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from rest_framework.exceptions import PermissionDenied 3 | from rest_framework.generics import ListAPIView 4 | from rest_framework.permissions import IsAuthenticated 5 | from rest_framework.throttling import UserRateThrottle 6 | 7 | from api.views.pagination import StandardResultsSetPagination 8 | from contest.models import Contest 9 | from submission.models import Submission 10 | from utils.permission import is_contest_manager 11 | 12 | 13 | class SubmissionRecordSerializer(serializers.ModelSerializer): 14 | class Meta: 15 | model = Submission 16 | fields = ("author_id", "contest_id", "problem_id", "create_time", "status") 17 | 18 | 19 | class SubmissionListView(ListAPIView): 20 | permission_classes = (IsAuthenticated,) 21 | throttle_classes = (UserRateThrottle,) 22 | serializer_class = SubmissionRecordSerializer 23 | pagination_class = StandardResultsSetPagination 24 | 25 | def get_queryset(self): 26 | contest_id = self.request.data.get("contest") 27 | if contest_id: 28 | contest = Contest.objects.get(contest_id) 29 | if is_contest_manager(self.request.user, contest): 30 | submission_set = contest.submission_set.all() 31 | else: 32 | raise PermissionDenied 33 | else: 34 | submission_set = Submission.objects.select_related("problem").filter(problem__visible=True).all() 35 | return submission_set.only("author_id", "contest_id", "problem_id", "create_time", "status") 36 | -------------------------------------------------------------------------------- /problem/testlib/generator/gen-tree-graph.cpp: -------------------------------------------------------------------------------- 1 | #include "testlib.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #define forn(i, n) for (int i = 0; i < int(n); i++) 26 | 27 | using namespace std; 28 | 29 | int main(int argc, char* argv[]) 30 | { 31 | registerGen(argc, argv, 1); 32 | 33 | int n = atoi(argv[1]); 34 | int t = atoi(argv[2]); 35 | 36 | vector p(n); 37 | forn(i, n) 38 | if (i > 0) 39 | p[i] = rnd.wnext(i, t); 40 | 41 | printf("%d\n", n); 42 | vector perm(n); 43 | forn(i, n) 44 | perm[i] = i; 45 | shuffle(perm.begin() + 1, perm.end()); 46 | vector > edges; 47 | 48 | for (int i = 1; i < n; i++) 49 | if (rnd.next(2)) 50 | edges.push_back(make_pair(perm[i], perm[p[i]])); 51 | else 52 | edges.push_back(make_pair(perm[p[i]], perm[i])); 53 | 54 | shuffle(edges.begin(), edges.end()); 55 | 56 | for (int i = 0; i + 1 < n; i++) 57 | printf("%d %d\n", edges[i].first + 1, edges[i].second + 1); 58 | 59 | return 0; 60 | } 61 | -------------------------------------------------------------------------------- /problem/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | 3 | import problem.views as v 4 | 5 | app_name = "problem" 6 | 7 | urlpatterns = [ 8 | url(r'^$', v.RuledRedirectView.as_view(), name='index'), 9 | url(r'^list/$', v.ProblemList.as_view(), name='list'), 10 | url(r'^(?P\d+)/$', v.ProblemView.as_view(), name='detail'), 11 | url(r'^(?P\d+)/submit/$', v.ProblemSubmitView.as_view(), name='submit'), 12 | url(r'^(?P\d+)/tags/edit/$', v.ProblemUpdateTags.as_view(), name='update_tags'), 13 | url(r'^(?P\d+)/discussion/$', v.DiscussionView.as_view(), name='discussion'), 14 | url(r'^(?P\d+)/submission/(?P\d+)/api/$', v.ProblemSubmissionAPI.as_view(), name='submission_api'), 15 | url(r'^(?P\d+)/submission/(?P\d+)/$', v.ProblemSubmissionView.as_view(), name='submission'), 16 | url(r'^(?P\d+)/submission/past/$', v.ProblemPersonalOlderSubmissionsAPI.as_view(), name='past_submission'), 17 | url(r'^status/$', v.StatusList.as_view(), name='status'), 18 | url(r'^millionaire/$', v.Millionaires.as_view(), name='millionaire'), 19 | url(r'^source/$', v.SourceList.as_view(), name='source'), 20 | url(r'^(?P\d+)/statistics/$', v.ProblemStatisticsView.as_view(), name='statistics'), 21 | url(r'^compare/$', v.compare_with, name='compare_with'), 22 | url(r'^recommending/$', v.ProblemRecommendation.as_view(), name='recommendation'), 23 | url(r'^feedback/compare/$', v.ProblemFeedbackCompare.as_view(), name='feedback_compare'), 24 | url(r'^reward/$', v.ProblemReward.as_view(), name='reward'), 25 | ] 26 | -------------------------------------------------------------------------------- /static/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eoj3", 3 | "version": "0.1.0", 4 | "description": "", 5 | "main": "gulpfile.js", 6 | "devDependencies": { 7 | "gulp": "^3.9.1", 8 | "gulp-autoprefixer": "^3.1.0", 9 | "gulp-cssmin": "^0.2.0", 10 | "gulp-less": "^3.5.0", 11 | "gulp-load-plugins": "^1.1.0", 12 | "gulp-plumber": "^1.1.0", 13 | "hover.css": "^2.2.0" 14 | }, 15 | "scripts": { 16 | "start": "gulp", 17 | "build": "gulp less" 18 | }, 19 | "author": "", 20 | "license": "ISC", 21 | "dependencies": { 22 | "@antv/data-set": "^0.8.3", 23 | "@antv/g2": "^3.0.4-beta.2", 24 | "@antv/g6": "3.0.2", 25 | "@antv/hierarchy": "^0.5.0", 26 | "ace-builds": "^1.4.1", 27 | "cal-heatmap": "^3.6.2", 28 | "clipboard": "^1.7.1", 29 | "django-channels": "^1.1.5", 30 | "echarts": "^3.7.1", 31 | "font-awesome": "^4.7.0", 32 | "jquery": "^3.5.0", 33 | "jquery-address": "^1.6.0", 34 | "jquery.scrollto": "^2.1.2", 35 | "js-cookie": "^2.1.4", 36 | "lodash": "^4.17.15", 37 | "mathjax": "^3.0.5", 38 | "moment": "^2.18.1", 39 | "morris.js": "^0.5.0", 40 | "npm-font-open-sans": "^1.0.3", 41 | "raphael": "^2.2.7", 42 | "semantic-ui-calendar": "0.0.8", 43 | "semantic-ui-less": "github:ultmaster/Semantic-UI-LESS", 44 | "simplemde": "^1.11.2", 45 | "sortablejs": "^1.6.0", 46 | "spectre.css": "^0.2.14", 47 | "typed.js": "^2.0.2", 48 | "underscore": "^1.8.3", 49 | "vue": "^2.4.1", 50 | "vuedraggable": "^2.14.1" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /templates/base.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'html_base.jinja2' %} 2 | 3 | {% block _content %} 4 | 5 | {% block broadcast_content %}{% endblock %} 6 | {% include 'components/navbar.jinja2' %} 7 |
8 |
9 | {% if not hide_header %} 10 |

{% block page_header %}#page_header{% endblock %}

11 | {% endif %} 12 | {% if notifications %} 13 |
14 | {% for item in notifications %} 15 |
16 | 17 | {{ username_display(item.actor) }} {{ item.verb }} 18 | {% if item.target.__class__.__name__ == 'Contest' %} 19 | {{ item.target.title }} 20 | {% if item.description %} 21 |
{{ item.description }} 22 | {% endif %} 23 | {% elif item.target.__class__.__name__ == 'Problem' %} 24 | {{ item.target.title }} 25 | {% elif item.target.__class__.__name__ == 'Blog' %} 26 | {{ item.target.title }} 27 | {% endif %} 28 |
29 | {% endfor %} 30 |
31 | {% endif %} 32 | {% block content %} 33 | {% endblock %} 34 |
35 | {% include 'components/footer.jinja2' %} 36 |
37 | 38 | {% endblock %} -------------------------------------------------------------------------------- /migrate/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | from submission.util import SubmissionStatus, STATUS_CHOICE 4 | from utils.language import LANG_CHOICE 5 | 6 | 7 | class OldSubmission(models.Model): 8 | lang = models.CharField(max_length=12, choices=LANG_CHOICE, default='cpp') 9 | code = models.TextField(blank=True) 10 | problem = models.IntegerField() 11 | author = models.CharField(max_length=192) 12 | 13 | create_time = models.DateTimeField(auto_now_add=True) 14 | judge_start_time = models.DateTimeField(blank=True, null=True) 15 | judge_end_time = models.DateTimeField(blank=True, null=True) 16 | 17 | status = models.IntegerField(choices=STATUS_CHOICE, default=SubmissionStatus.WAITING) 18 | status_percent = models.IntegerField(default=0) 19 | status_detail = models.TextField(blank=True) 20 | status_time = models.IntegerField(default=0) 21 | status_memory = models.IntegerField(default=0) 22 | code_length = models.IntegerField(default=0) 23 | 24 | class Meta: 25 | ordering = ["-pk"] 26 | 27 | def get_verdict_color(self): 28 | return '' 29 | 30 | 31 | class OldUser(models.Model): 32 | username = models.CharField(max_length=192) 33 | password = models.CharField(max_length=192) 34 | school = models.CharField(max_length=192) 35 | email = models.CharField(max_length=192) 36 | 37 | 38 | class OldDiscussion(models.Model): 39 | text = models.TextField() 40 | author = models.CharField(max_length=192) 41 | create_time = models.DateTimeField(auto_now_add=True) 42 | problem = models.IntegerField() 43 | -------------------------------------------------------------------------------- /templates/problem/detail/tag.jinja2: -------------------------------------------------------------------------------- 1 | {% if public_edit_access %} 2 |
3 |
4 |
标签
5 |
6 |
7 |
8 |
9 | {% csrf_token %} 10 | 11 |
12 | 22 |
23 | 24 |
25 |
26 |
27 |
28 | {% elif show_tags %} 29 |
30 |
31 |
题目标签
32 |
33 |
34 |
35 | {% for tag in tags_list %} 36 | {{ tag }} 37 | {% endfor %} 38 |
39 |
40 |
41 | {% endif %} -------------------------------------------------------------------------------- /templates/problem/feedback/compare.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'base.jinja2' %} 2 | {% from 'components/problem.jinja2' import problem_view with context %} 3 | 4 | {% block page_header %}题目难度比较{% endblock %} 5 | 6 | {% block content %} 7 | 8 |
9 |
10 |
11 | {% csrf_token %} 12 | 13 | 14 | 15 |
16 |
17 | 20 |
21 |
22 | {% csrf_token %} 23 | 24 | 25 | 26 |
27 |
28 |
29 |
{{ problem1.id }}. {{ problem1.title }}
30 | {{ problem_view(problem1, False) }} 31 |
32 |
33 |
{{ problem2.id }}. {{ problem2.title }}
34 | {{ problem_view(problem2, False) }} 35 |
36 |
37 | 38 | {% endblock %} 39 | 40 | -------------------------------------------------------------------------------- /templates/contest/balloon.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'contest/base.jinja2' %} 2 | {% from 'components/status.jinja2' import status with context %} 3 | 4 | {% block contest_title %}Balloon - {% endblock %} 5 | {% block contest_content %} 6 | 7 | {% include 'components/message.jinja2' %} 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | {% for balloon in balloon_list %} 20 | 21 | 22 | 23 | 24 | 33 | 34 | {% endfor %} 35 | 36 |
NameProblemTimeStatus
{{ balloon.username }}{{ balloon.contest_problem.identifier }}{{ balloon.create_time | date("H:i:s") }}{% if balloon.ok %} 25 | OK 26 | {% else %} 27 | {% set form_id = "form%d" % balloon.pk %} 28 |
29 | {% csrf_token %} 30 | Waiting 31 |
32 | {% endif %}
37 | 38 | {% endblock %} -------------------------------------------------------------------------------- /templates/backstage/blog/blog.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'backstage/base.jinja2' %} 2 | 3 | {% block content_header %} 4 | 博客 5 | {% endblock %} 6 | 7 | {% block backstage_content %} 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | {% for blog in blog_list %} 23 | 24 | 29 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | {% endfor %} 41 | 42 |
可见首页推荐#作者标题创建更新
25 |
26 | 27 |
28 |
30 |
31 | 32 |
33 |
{{ blog.pk }}{{ blog.author.username }}{{ blog.title[:10] }}{{ blog.create_time | date('Y-m-d H:i:s') }}{{ blog.edit_time | date('Y-m-d H:i:s') }}
43 | 44 | {{ my_paginator() }} 45 | 46 | {% endblock %} 47 | -------------------------------------------------------------------------------- /templates/backstage/problem/skill.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'backstage/base.jinja2' %} 2 | 3 | {% block content_header %} 4 | Problem Tags 5 | {% endblock %} 6 | 7 | {% block backstage_content %} 8 | 9 | 10 | 11 | 12 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | {% for skill in skill_list %} 29 | 30 | 31 | 32 | 33 | 34 | 35 | 38 | 39 | {% endfor %} 40 | 41 |
13 | 16 |
#NameDescriptionProblemsParentEdit
{{ skill.pk }}{{ skill.name }}{{ skill.description }}{{ skill.problem_list }}{{ skill.parent_id }} 36 | Edit 37 |
42 | 43 | {% call modal(id="archive-add", title="Add a skill", action=url('backstage:archive_add')) %} 44 | {% csrf_token %} 45 |
46 | 47 | 48 |
49 | {% endcall %} 50 | 51 | {% endblock %} 52 | -------------------------------------------------------------------------------- /polygon/templates/polygon/contest/anticheat.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'polygon/contest/base.jinja2' %} 2 | {% from 'components/status.jinja2' import status with context %} 3 | 4 | {% block contest_content %} 5 | 6 |

7 | Start Analysis 8 |

9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | {% for plag in plag_list %} 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | {% endfor %} 38 | 39 |
#StatusProblemCreatedUpdatedLanguageKeepReport
{{ plag.pk }}{{ plag.get_status_display() }}{{ plag.identifier }}{{ plag.create_time | date("Y-m-d H:i:s") }}{{ plag.update_time | date("Y-m-d H:i:s") }}{{ plag.language }}{{ plag.keep_match }}StderrStdoutIndex
40 | 41 | 42 | {% endblock %} -------------------------------------------------------------------------------- /polygon/templates/polygon/problem2/statement/preview.jinja2: -------------------------------------------------------------------------------- 1 | {% extends 'polygon/problem2/base.jinja2' %} 2 | 3 | {% block problem_content %} 4 | 5 |

{{ statement.title }}

6 |
7 | {% if statement.description %} 8 |
9 | {{ statement.description | markdown | safe }} 10 |
11 | {% endif %} 12 | {% if statement.input %} 13 |

Input

14 | {{ statement.input | markdown | safer | safe }} 15 | {% endif %} 16 | {% if statement.output %} 17 |

Output

18 | {{ statement.output | markdown | safer | safe }} 19 | {% endif %} 20 | {% if samples %} 21 |
22 |

Examples

23 | {% for input, output in samples %} 24 |
25 |
26 |
Input
27 |
{{ input }}
28 |
29 |
30 |
Output
31 |
{{ output }}
32 |
33 |
34 | {% endfor %} 35 |
36 | {% endif %} 37 | {% if statement.hint %} 38 |

Note

39 | {{ statement.hint | markdown | safer | safe }} 40 | {% endif %} 41 |
42 | 43 | 44 | {% endblock %} 45 | --------------------------------------------------------------------------------