Skip to content

Commit

Permalink
NONE: improve documents
Browse files Browse the repository at this point in the history
  • Loading branch information
kmyk committed Feb 19, 2019
1 parent ebab7c6 commit c75b006
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 28 deletions.
62 changes: 44 additions & 18 deletions onlinejudge/dispatch.py
Original file line number Diff line number Diff line change
@@ -1,54 +1,80 @@
# Python Version: 3.x
"""
.. py:data:: services
:type: :py:class:`List` [ :py:class:`Type` [ :py:class:`onlinejudge.type.Service` ] ]
contains classes to use for :py:func:`service_from_url`
.. py:data:: problems
:type: :py:class:`List` [ :py:class:`Type` [ :py:class:`onlinejudge.type.Problem` ] ]
contains classes to use for :py:func:`problem_from_url`
.. py:data:: submissions
:type: :py:class:`List` [ :py:class:`Type` [ :py:class:`onlinejudge.type.Submission` ] ]
contains classes to use for :py:func:`submission_from_url`
"""

from typing import TYPE_CHECKING, List, Optional, Type

import onlinejudge.implementation.logging as log

if TYPE_CHECKING:
from onlinejudge.type import Service, Problem, Submission
from onlinejudge.type import Service, Problem, Submission

submissions = [] # type: List[Type['Submission']]


def submission_from_url(s: str) -> Optional['Submission']:
def submission_from_url(url: str) -> Optional[Submission]:
for cls in submissions:
submission = cls.from_url(s)
submission = cls.from_url(url)
if submission is not None:
log.status('submission recognized: %s: %s', str(submission), s)
log.status('submission recognized: %s: %s', str(submission), url)
return submission
log.failure('unknown submission: %s', s)
log.failure('unknown submission: %s', url)
return None


problems = [] # type: List[Type['Problem']]


def problem_from_url(s: str) -> Optional['Problem']:
def problem_from_url(url: str) -> Optional[Problem]:
"""
>>> onlinejudge.dispatch.problem_from_url("https://atcoder.jp/contests/abc077/tasks/arc084_b")
<onlinejudge.service.atcoder.AtCoderProblem object at 0x7fa0538ead68>
>>> onlinejudge.dispatch.problem_from_url("https://codeforces.com/contest/1012/problem/D")
<onlinejudge.service.codeforces.CodeforcesProblem object at 0x7fa05a916710>
"""

for cls in problems:
problem = cls.from_url(s)
problem = cls.from_url(url)
if problem is not None:
log.status('problem recognized: %s: %s', str(problem), s)
log.status('problem recognized: %s: %s', str(problem), url)
return problem
submission = submission_from_url(s)
submission = submission_from_url(url)
if submission is not None:
return submission.get_problem()
log.failure('unknown problem: %s', s)
log.failure('unknown problem: %s', url)
return None


services = [] # type: List[Type['Service']]


def service_from_url(s: str) -> Optional['Service']:
def service_from_url(url: str) -> Optional[Service]:
for cls in services:
service = cls.from_url(s)
service = cls.from_url(url)
if service is not None:
log.status('service recognized: %s: %s', str(service), s)
log.status('service recognized: %s: %s', str(service), url)
return service
submission = submission_from_url(s)
submission = submission_from_url(url)
if submission is not None:
return submission.get_service()
problem = problem_from_url(s)
problem = problem_from_url(url)
if problem is not None:
return problem.get_service()
log.failure('unknown service: %s', s)
log.failure('unknown service: %s', url)
return None
64 changes: 56 additions & 8 deletions onlinejudge/service/atcoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@
from onlinejudge.type import SubmissionError


# This is a workaround. AtCoder's servers sometime fail to send "Content-Type" field.
# see https://github.com/kmyk/online-judge-tools/issues/28 and https://github.com/kmyk/online-judge-tools/issues/232
def _request(*args, **kwargs):
"""
This is a workaround. AtCoder's servers sometime fail to send "Content-Type" field.
see https://github.com/kmyk/online-judge-tools/issues/28 and https://github.com/kmyk/online-judge-tools/issues/232
"""
resp = utils.request(*args, **kwargs)
log.debug('AtCoder\'s server said "Content-Type: %s"', resp.headers.get('Content-Type', '(not sent)'))
resp.encoding = 'UTF-8'
Expand Down Expand Up @@ -60,10 +62,15 @@ def get_name(self) -> str:
return 'atcoder'

@classmethod
def from_url(cls, s: str) -> Optional['AtCoderService']:
# example: https://atcoder.jp/
# example: http://agc012.contest.atcoder.jp/
result = urllib.parse.urlparse(s)
def from_url(cls, url: str) -> Optional['AtCoderService']:
"""
:param url: example:
- https://atcoder.jp/
- http://agc012.contest.atcoder.jp/
"""

result = urllib.parse.urlparse(url)
if result.scheme in ('', 'http', 'https') \
and (result.netloc in ('atcoder.jp', 'beta.atcoder.jp') or result.netloc.endswith('.contest.atcoder.jp')):
return cls()
Expand Down Expand Up @@ -101,7 +108,13 @@ def _report_messages(cls, msgs: List[str], unexpected: bool = False) -> bool:
return bool(msgs)

def iterate_contests(self, lang: str = 'ja', session: Optional[requests.Session] = None) -> Generator['AtCoderContest', None, None]:
assert lang in ('ja', 'en') # NOTE: "lang=ja" is required to see some Japanese-local contests. However you can use "lang=en" to see the English names of contests.
"""
:param lang: must be `ja` (default) or `en`.
:note: `lang=ja` is required to see some Japanese-local contests.
:note: You can use `lang=en` to see the English names of contests.
"""

assert lang in ('ja', 'en')
session = session or utils.new_default_session()
last_page = None
for page in itertools.count(1): # 1-based
Expand All @@ -124,6 +137,10 @@ def get_user_history_url(self, user_id: str) -> str:


class AtCoderContest(object):
"""
:ivar contest_id: :py:class:`str`
"""

def __init__(self, contest_id: str):
self.contest_id = contest_id

Expand All @@ -136,6 +153,13 @@ def __init__(self, contest_id: str):

@classmethod
def from_url(cls, url: str) -> Optional['AtCoderContest']:
"""
:param url: example:
- https://kupc2014.contest.atcoder.jp/tasks/kupc2014_d
- https://atcoder.jp/contests/agc030
"""

result = urllib.parse.urlparse(url)

# example: https://kupc2014.contest.atcoder.jp/tasks/kupc2014_d
Expand Down Expand Up @@ -224,7 +248,12 @@ def list_problems(self, session: Optional[requests.Session] = None) -> List['AtC


class AtCoderProblem(onlinejudge.type.Problem):
# AtCoder has problems independently from contests. Therefore the notions "contest_id", "alphabet", and "url" don't belong to problems itself.
"""
:ivar contest_id: :py:class:`str`
:ivar problem_id: :py:class:`str`
:note: AtCoder has problems independently from contests. Therefore the notions `contest_id`, `alphabet`, and `url` don't belong to problems itself.
"""

def __init__(self, contest_id: str, problem_id: str):
self.contest_id = contest_id
Expand Down Expand Up @@ -460,6 +489,11 @@ def _load_details(self, session: Optional[requests.Session] = None) -> int:


class AtCoderSubmission(onlinejudge.type.Submission):
"""
:ivar contest_id: :py:class:`str`
:ivar submission_id: :py:class:`str`
"""

def __init__(self, contest_id: str, submission_id: int, problem_id: Optional[str] = None):
self.contest_id = contest_id
self.submission_id = submission_id
Expand Down Expand Up @@ -603,6 +637,13 @@ def get_problem(self, session: Optional[requests.Session] = None) -> AtCoderProb


class AtCoderSubmissionTestSet(object):
"""
:ivar set_name: :py:class:`str`
:ivar score: :py:class:`int`
:ivar max_score: :py:class:`int`
:ivar test_case_names: :py:class:`List` [ :py:class:`str` ]
"""

def __init__(self, set_name: str, score: int, max_score: int, test_case_names: List[str]):
self.set_name = set_name
self.score = score
Expand All @@ -620,6 +661,13 @@ def _from_table_row(cls, tr: bs4.Tag) -> 'AtCoderSubmissionTestSet':


class AtCoderSubmissionTestCaseResult(object):
"""
:ivar case_name: :py:class:`str`
:ivar status: :py:class:`str`
:ivar exec_time_msec: :py:class:`int` in millisecond
:ivar memory_byte: :py:class:`int` in byte
"""

def __init__(self, case_name: str, status: str, exec_time_msec: Optional[int], memory_byte: Optional[int]):
self.case_name = case_name
self.status = status
Expand Down
6 changes: 6 additions & 0 deletions onlinejudge/service/codeforces.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ def from_url(cls, s: str) -> Optional['CodeforcesService']:

# NOTE: Codeforces has its API: https://codeforces.com/api/help
class CodeforcesProblem(onlinejudge.type.Problem):
"""
:ivar contest_id: :py:class:`str`
:ivar index: :py:class:`int`
:ivar kind: :py:class:`str` must be `contest` or `gym`
"""

def __init__(self, contest_id: int, index: str, kind: Optional[str] = None):
assert isinstance(contest_id, int)
assert index in string.ascii_uppercase
Expand Down
7 changes: 5 additions & 2 deletions onlinejudge/type.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,10 @@ def download_sample_cases(self, session: Optional['requests.Session'] = None) ->
def download_system_cases(self, session: Optional['requests.Session'] = None) -> List[TestCase]:
raise NotImplementedError

def submit_code(self, code: bytes, language: str, session: Optional['requests.Session'] = None) -> 'Submission': # or SubmissionError
def submit_code(self, code: bytes, language: str, session: Optional['requests.Session'] = None) -> 'Submission':
"""
:raises SubmissionError:
"""
raise NotImplementedError

def get_language_dict(self, session: Optional['requests.Session'] = None) -> Dict[str, Language]:
Expand All @@ -67,7 +70,7 @@ def from_url(self, s: str) -> Optional['Problem']:


class Submission(object):
def download_code(self, session: Optional['requests.Session'] = None) -> str:
def download_code(self, session: Optional['requests.Session'] = None) -> bytes:
raise NotImplementedError

def get_url(self) -> str:
Expand Down

0 comments on commit c75b006

Please sign in to comment.