Skip to content

String pid #514

New issue

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

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

Already on GitHub? Sign in to your account

Open
wants to merge 30 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
fac3a93
String PID support
undefined-moe May 13, 2019
316f5ce
fix spaces
undefined-moe May 13, 2019
a6c531a
String PID validator
undefined-moe May 13, 2019
0515be6
Help-Wanted:
undefined-moe May 13, 2019
68ce0c9
Fix tests
undefined-moe May 13, 2019
43755a0
UnittestFix1
undefined-moe May 13, 2019
38b7ec0
This is a backup version and it's **not** finished
undefined-moe May 14, 2019
d3bdbc8
0x02
undefined-moe May 14, 2019
536dc33
0x03
undefined-moe May 14, 2019
e5af567
0x04
undefined-moe May 14, 2019
fde8f43
0x05
undefined-moe May 14, 2019
bbbedc5
Merge branch 'master' into stringPID
undefined-moe May 14, 2019
7778e02
0x06
undefined-moe May 14, 2019
10b3ecd
0x07
undefined-moe May 14, 2019
60e6374
0x08
undefined-moe May 14, 2019
35e4aef
0x09
undefined-moe May 15, 2019
7a8bd75
0x0a
undefined-moe May 15, 2019
c1d83c3
Merge branch 'master' into stringPID
undefined-moe May 15, 2019
6c2fd17
0x0b
undefined-moe May 15, 2019
2d66a76
0x0c
undefined-moe May 15, 2019
a18e83a
0x0d
undefined-moe May 16, 2019
c6cf315
fix solution
undefined-moe May 24, 2019
e3e2e6f
bug fix
undefined-moe May 27, 2019
523cf83
Merge pull request #1 from vijos/master
undefined-moe May 27, 2019
8e9b68c
Merge branch 'master' into stringPID
undefined-moe May 29, 2019
3132019
to lowercase
undefined-moe May 29, 2019
69e7e62
Merge branch 'stringPID' of https://github.com/masnn0/vj4 into stringPID
undefined-moe May 29, 2019
c2838e9
Merge branch 'master' into stringPID
undefined-moe May 31, 2019
35c7080
Merge pull request #3 from vijos/master
undefined-moe May 31, 2019
27da508
Merge pull request #4 from masnn0/master
undefined-moe May 31, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions vj4/handler/contest.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from vj4.util import pagination


@app.route('/contest', 'contest_main')
@app.route('/c', 'contest_main')
class ContestMainHandler(contest.ContestMixin, base.Handler):
CONTESTS_PER_PAGE = 20

Expand All @@ -47,7 +47,7 @@ async def get(self, *, rule: int=0, page: int=1):
tdocs=tdocs, tsdict=tsdict)


@app.route('/contest/{tid:\w{24}}', 'contest_detail')
@app.route('/c/{tid:\w{24}}', 'contest_detail')
class ContestDetailHandler(contest.ContestMixin, base.OperationHandler):
DISCUSSIONS_PER_PAGE = 15

Expand Down Expand Up @@ -105,7 +105,7 @@ async def post_attend(self, *, tid: objectid.ObjectId):
self.json_or_redirect(self.url)


@app.route('/contest/{tid:\w{24}}/code', 'contest_code')
@app.route('/c/{tid:\w{24}}/code', 'contest_code')
class ContestCodeHandler(base.OperationHandler):
@base.limit_rate('contest_code', 3600, 60)
@base.route_argument
Expand All @@ -132,7 +132,7 @@ async def get(self, *, tid: objectid.ObjectId):
file_name='{}.zip'.format(tdoc['title']))


@app.route('/contest/{tid}/{pid:-?\d+|\w{24}}', 'contest_detail_problem')
@app.route('/c/{tid}/p/{pid:[a-zA-Z0-9]+}', 'contest_detail_problem')
class ContestDetailProblemHandler(contest.ContestMixin, base.Handler):
@base.route_argument
@base.require_perm(builtin.PERM_VIEW_CONTEST)
Expand Down Expand Up @@ -163,7 +163,7 @@ async def get(self, *, tid: objectid.ObjectId, pid: document.convert_doc_id):
page_title=pdoc['title'], path_components=path_components)


@app.route('/contest/{tid}/{pid}/submit', 'contest_detail_problem_submit')
@app.route('/c/{tid}/p/{pid}/submit', 'contest_detail_problem_submit')
class ContestDetailProblemSubmitHandler(contest.ContestMixin, base.Handler):
@base.route_argument
@base.require_perm(builtin.PERM_VIEW_CONTEST)
Expand Down Expand Up @@ -233,7 +233,7 @@ async def post(self, *, tid: objectid.ObjectId, pid: document.convert_doc_id,
self.json_or_redirect(self.reverse_url('record_detail', rid=rid))


@app.route('/contest/{tid}/scoreboard', 'contest_scoreboard')
@app.route('/c/{tid}/scoreboard', 'contest_scoreboard')
class ContestScoreboardHandler(contest.ContestMixin, base.Handler):
@base.route_argument
@base.require_perm(builtin.PERM_VIEW_CONTEST)
Expand All @@ -251,7 +251,7 @@ async def get(self, *, tid: objectid.ObjectId):
page_title=page_title, path_components=path_components)


@app.route('/contest/{tid}/scoreboard/download/{ext}', 'contest_scoreboard_download')
@app.route('/c/{tid}/scoreboard/download/{ext}', 'contest_scoreboard_download')
class ContestScoreboardDownloadHandler(contest.ContestMixin, base.Handler):
def _export_status_as_csv(self, rows):
# \r\n for notepad compatibility
Expand Down Expand Up @@ -321,7 +321,7 @@ async def post(self, *, title: str, content: str, rule: int,
self.json_or_redirect(self.reverse_url('contest_detail', tid=tid))


@app.route('/contest/{tid}/edit', 'contest_edit')
@app.route('/c/{tid}/edit', 'contest_edit')
class ContestEditHandler(contest.ContestMixin, base.Handler):
@base.route_argument
@base.require_priv(builtin.PRIV_USER_PROFILE)
Expand Down
16 changes: 8 additions & 8 deletions vj4/handler/homework.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def _format_penalty_rules_yaml(penalty_rules):
return yaml_doc


@app.route('/homework', 'homework_main')
@app.route('/h', 'homework_main')
class HomeworkMainHandler(contest.ContestMixin, base.Handler):
@base.require_perm(builtin.PERM_VIEW_HOMEWORK)
async def get(self):
Expand All @@ -72,7 +72,7 @@ async def get(self):
self.render('homework_main.html', tdocs=tdocs, calendar_tdocs=calendar_tdocs)


@app.route('/homework/{tid:\w{24}}', 'homework_detail')
@app.route('/h/{tid:\w{24}}', 'homework_detail')
class HomeworkDetailHandler(contest.ContestMixin, base.OperationHandler):
DISCUSSIONS_PER_PAGE = 15

Expand Down Expand Up @@ -130,7 +130,7 @@ async def post_attend(self, *, tid: objectid.ObjectId):
self.json_or_redirect(self.url)


@app.route('/homework/{tid:\w{24}}/code', 'homework_code')
@app.route('/h/{tid:\w{24}}/code', 'homework_code')
class HomeworkCodeHandler(base.OperationHandler):
@base.limit_rate('homework_code', 3600, 60)
@base.route_argument
Expand All @@ -157,7 +157,7 @@ async def get(self, *, tid: objectid.ObjectId):
file_name='{}.zip'.format(tdoc['title']))


@app.route('/homework/{tid}/{pid:-?\d+|\w{24}}', 'homework_detail_problem')
@app.route('/h/{tid}/p/{pid:[a-zA-Z0-9]+}', 'homework_detail_problem')
class HomeworkDetailProblemHandler(contest.ContestMixin, base.Handler):
@base.route_argument
@base.require_perm(builtin.PERM_VIEW_HOMEWORK)
Expand Down Expand Up @@ -188,7 +188,7 @@ async def get(self, *, tid: objectid.ObjectId, pid: document.convert_doc_id):
page_title=pdoc['title'], path_components=path_components)


@app.route('/homework/{tid}/{pid}/submit', 'homework_detail_problem_submit')
@app.route('/h/{tid}/p/{pid}/submit', 'homework_detail_problem_submit')
class HomeworkDetailProblemSubmitHandler(contest.ContestMixin, base.Handler):
@base.route_argument
@base.require_perm(builtin.PERM_VIEW_HOMEWORK)
Expand Down Expand Up @@ -257,7 +257,7 @@ async def post(self, *, tid: objectid.ObjectId, pid: document.convert_doc_id,
self.json_or_redirect(self.reverse_url('record_detail', rid=rid))


@app.route('/homework/{tid}/scoreboard', 'homework_scoreboard')
@app.route('/h/{tid}/scoreboard', 'homework_scoreboard')
class HomeworkScoreboardHandler(contest.ContestMixin, base.Handler):
@base.route_argument
@base.require_perm(builtin.PERM_VIEW_HOMEWORK)
Expand All @@ -275,7 +275,7 @@ async def get(self, *, tid: objectid.ObjectId):
page_title=page_title, path_components=path_components)


@app.route('/homework/{tid}/scoreboard/download/{ext}', 'homework_scoreboard_download')
@app.route('/h/{tid}/scoreboard/download/{ext}', 'homework_scoreboard_download')
class HomeworkScoreboardDownloadHandler(contest.ContestMixin, base.Handler):
def _export_status_as_csv(self, rows):
# \r\n for notepad compatibility
Expand Down Expand Up @@ -357,7 +357,7 @@ async def post(self, *, title: str, content: str,
self.json_or_redirect(self.reverse_url('homework_detail', tid=tid))


@app.route('/homework/{tid}/edit', 'homework_edit')
@app.route('/h/{tid}/edit', 'homework_edit')
class HomeworkEditHandler(contest.ContestMixin, base.Handler):
@base.require_priv(builtin.PRIV_USER_PROFILE)
@base.require_perm(builtin.PERM_EDIT_HOMEWORK)
Expand Down
18 changes: 8 additions & 10 deletions vj4/handler/problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ async def star_unstar(self, *, pid: document.convert_doc_id, star: bool):
post_unstar = functools.partialmethod(star_unstar, star=False)


@app.route('/p/random', 'problem_random')
@app.route('/problem/random', 'problem_random')
class ProblemRandomHandler(base.Handler):
@base.require_perm(builtin.PERM_VIEW_PROBLEM)
@base.route_argument
Expand Down Expand Up @@ -185,7 +185,7 @@ async def get(self, *, category: str):
self.json_or_redirect(self.referer_or_main)


@app.route('/p/{pid:-?\d+|\w{24}}', 'problem_detail')
@app.route('/p/{pid:[a-zA-Z0-9]+}', 'problem_detail')
class ProblemDetailHandler(base.OperationHandler):
async def _get_related_trainings(self, pid):
if self.has_perm(builtin.PERM_VIEW_TRAINING):
Expand Down Expand Up @@ -568,7 +568,7 @@ async def get(self, *, pid: document.convert_doc_id):
secret=fdoc['metadata']['secret']))


@app.route('/p/create', 'problem_create')
@app.route('/problem/create', 'problem_create')
class ProblemCreateHandler(base.Handler):
@base.require_priv(builtin.PRIV_USER_PROFILE)
@base.require_perm(builtin.PERM_CREATE_PROBLEM)
Expand All @@ -580,16 +580,14 @@ async def get(self):
@base.post_argument
@base.require_csrf_token
@base.sanitize
async def post(self, *, title: str, content: str, hidden: bool=False, numeric_pid: bool=False):
pid = None
if numeric_pid:
pid = await domain.inc_pid_counter(self.domain_id)
async def post(self, *, title: str, content: str, hidden: bool=False, pname: str=None):
pid = await domain.inc_pid_counter(self.domain_id)
pid = await problem.add(self.domain_id, title, content, self.user['_id'],
hidden=hidden, pid=pid)
hidden=hidden, pid=pid, pname=pname)
self.json_or_redirect(self.reverse_url('problem_settings', pid=pid))


@app.route('/p/copy', 'problem_copy')
@app.route('/problem/copy', 'problem_copy')
class ProblemCopyHandler(base.Handler):
MAX_PROBLEMS_PER_REQUEST = 20

Expand Down Expand Up @@ -791,7 +789,7 @@ async def get(self, *, pid: document.convert_doc_id):
page_title=pdoc['title'], path_components=path_components)


@app.route('/p/search', 'problem_search')
@app.route('/problem/search', 'problem_search')
class ProblemSearchHandler(base.Handler):
@base.get_argument
@base.route_argument
Expand Down
3 changes: 2 additions & 1 deletion vj4/locale/zh_CN.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -773,7 +773,8 @@ display_name: 显示名
home_domain_account: 当前域的设置
'The value `{1}` of {0} already exists.': '{0} 的值 `{1}` 已经存在。'
Display name {1} you want to set is used by others.: 您想要设置的显示名 {1} 已经被其他人使用了。
Numeric PID: 数字题号
Leave blank to use numeric pid.: 留空以使用数字题号
PNAME: 题号
Only {0} problems can be copied in one request, got {1}.: 一次请求只能复制 {0} 个题目,但是您输入了 {1} 个。
Problem is successfully copied.: 题目复制完成。
problem_copy: 复制题目
Expand Down
14 changes: 11 additions & 3 deletions vj4/model/adaptor/problem.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,17 @@ def get_categories():


@argmethod.wrap
async def add(domain_id: str, title: str, content: str, owner_uid: int,
pid: document.convert_doc_id=None, data: objectid.ObjectId=None,
async def add(domain_id: str, title: str, content: str, owner_uid: int,
pid: document.convert_doc_id=None, pname: str=None, data: objectid.ObjectId=None,
category: list=[], tag: list=[], hidden: bool=False):
validator.check_title(title)
validator.check_content(content)
if not pname:
pname = str(pid)
else:
validator.check_string_pname(pname)
pid = await document.add(domain_id, content, owner_uid, document.TYPE_PROBLEM,
pid, title=title, data=data, category=category, tag=tag,
pid, pname=pname, title=title, data=data, category=category, tag=tag,
hidden=hidden, num_submit=0, num_accept=0)
await domain.inc_user(domain_id, owner_uid, num_problems=1)
return pid
Expand All @@ -65,6 +69,10 @@ async def copy(pdoc, dest_domain_id: str, owner_uid: int,

@argmethod.wrap
async def get(domain_id: str, pid: document.convert_doc_id, uid: int = None):
try:
pid = int(pid)
except ValueError:

pdoc = await document.get(domain_id, document.TYPE_PROBLEM, pid)
if not pdoc:
raise error.ProblemNotFoundError(domain_id, pid)
Expand Down
3 changes: 3 additions & 0 deletions vj4/model/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,9 @@ async def ensure_indexes():
('uid', 1),
('enroll', 1),
('doc_id', 1)], sparse=True)
await status_coll.create_indec([('domain_id', 1),
('doc_type', 1),
('pname', 1)], sparse=True)


if __name__ == '__main__':
Expand Down
2 changes: 1 addition & 1 deletion vj4/ui/templates/components/problem.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
{%- endif %}
>
{%- endif %}
{% if pdoc['doc_id']|string|length < 10 %}P{{ pdoc['doc_id'] }} {% endif %}{{ pdoc['title'] }}
{% if pdoc['pname'] %}{{ pdoc['pname'] }} {% elif pdoc['doc_id']|string|length < 24 %}P{{ pdoc['doc_id'] }} {% endif %}{{ pdoc['title'] }}
{%- if not invalid %}
</a>
{%- endif %}
Expand Down
12 changes: 5 additions & 7 deletions vj4/ui/templates/problem_edit.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,18 @@
<div class="section__body">
<form method="post">
<div class="row">
<div class="medium-8 columns">
<div class="medium-7 columns">
<label>
{{ _('Title') }}
<input name="title" placeholder="{{ _('title') }}" value="{{ pdoc['title']|default('') }}" class="textbox" autofocus>
</label>
</div>
{% if page_name == 'problem_create' %}
<div class="medium-2 columns">
<div class="medium-3 columns">
<label>
{{ _('Settings') }}
{{ _('PNAME') }}
<br>
<label class="checkbox">
<input type="checkbox" name="numeric_pid" value="on" checked>{{ _('Numeric PID') }}
<label>
<input name="pname" placeholder="{{ _('Leave blank to use numeric pid.') }}" class="textbox">
</label>
</label>
</div>
Expand All @@ -30,7 +29,6 @@
</label>
</label>
</div>
{% endif %}
</div>
<div class="row"><div class="columns">
<label>
Expand Down
10 changes: 10 additions & 0 deletions vj4/util/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
ID_RE = re.compile(r'[^\\/\s\u3000]([^\\/\n\r]*[^\\/\s\u3000])?')
ROLE_RE = re.compile(r'[_0-9A-Za-z]{1,256}')
DOMAIN_INVITATION_CODE_RE = re.compile(r'[0-9A-Za-z]{1,64}')
PNAME_RE = re.compile(r'[a-zA-Z]+[a-zA-Z0-9]*')


def is_uid(s):
Expand All @@ -23,6 +24,15 @@ def check_uid(s):
raise error.ValidationError('uid')


def is_string_pname(s):
return bool(PNAME_RE.fullmatch(s))


def check_string_pname(s):
if not is_string_pname(s):
raise error.ValidationError('pname')


def is_uname(s):
return bool(UNAME_RE.fullmatch(s))

Expand Down