Source code for onlinejudge.type

# Python Version: 3.x
import datetime
from abc import ABC, abstractmethod
from typing import Callable, Iterator, List, NamedTuple, NewType, Optional, Tuple

import requests

CredentialsProvider = Callable[[], Tuple[str, str]]


[docs]class LoginError(RuntimeError): pass
[docs]class Service(ABC):
[docs] def login(self, *, get_credentials: CredentialsProvider, session: Optional[requests.Session] = None) -> None: """ :param get_credentials: returns a tuple of (username, password) :raises LoginError: """ raise NotImplementedError
[docs] def get_url_of_login_page(self) -> str: raise NotImplementedError
[docs] def is_logged_in(self, *, session: Optional[requests.Session] = None) -> bool: raise NotImplementedError
[docs] @abstractmethod def get_url(self) -> str: raise NotImplementedError
[docs] @abstractmethod def get_name(self) -> str: """ example: - `AtCoder` - `Codeforces` - `PKU JudgeOnline` :note: If you want something like identifier (e.g. `atcoder`, `codeforces` or `poj`), you can use a domain obtained from :py:meth:`get_url`. """ raise NotImplementedError
def __repr__(self) -> str: return '{}.from_url({})'.format(self.__class__.__name__, repr(self.get_url())) def __eq__(self, other) -> bool: return self.__class__ == other.__class__ and self.get_url() == other.get_url()
[docs] @classmethod @abstractmethod def from_url(self, s: str) -> Optional['Service']: pass
[docs] def iterate_contests(self, *, session: Optional[requests.Session] = None) -> Iterator['Contest']: raise NotImplementedError
TestCase = NamedTuple('TestCase', [ ('name', str), ('input_name', str), ('input_data', bytes), ('output_name', str), ('output_data', bytes), ]) LanguageId = NewType('LanguageId', str) """ :note: This is just a :py:class:`NewType` -ed :py:class:`str` not, but you should not use this other than a label. """ Language = NamedTuple('Language', [ ('id', LanguageId), ('name', str), ]) """ :ivar id: :py:class:`LanguageId` :ivar name: :py:class:`str` """
[docs]class NotLoggedInError(RuntimeError): pass
[docs]class SampleParseError(RuntimeError): pass
[docs]class SubmissionError(RuntimeError): pass
[docs]class DownloadedData(ABC): """ :note: :py:class:`DownloadedData` and its subclasses represent contents which are obtained by network access. The values may depends your session. :py:class:`DownloadedData` とそのサブクラスは、ネットワークアクセスの結果得られるようなデータを表現します。その値はログイン状況などにより接続のたびに変化することがあります。 """ @property @abstractmethod def url(self) -> str: raise NotImplementedError @property def json(self) -> Optional[bytes]: return None @property def html(self) -> Optional[bytes]: return None @property def timestamp(self) -> Optional[datetime.datetime]: return None @property def session(self) -> Optional[requests.Session]: return None @property def response(self) -> Optional[requests.Response]: return None
[docs]class ContestData(DownloadedData):
[docs] def url(self) -> str: return self.contest.get_url()
@property @abstractmethod def contest(self) -> 'Contest': raise NotImplementedError @property def service(self) -> Service: return self.contest.get_service() @property @abstractmethod def name(self) -> str: raise NotImplementedError
[docs]class Contest(ABC): """ :note: :py:class:`Contest` represents just a URL of a contest, without the data of the contest. """
[docs] def list_problems(self, *, session: Optional[requests.Session] = None) -> List['Problem']: raise NotImplementedError
[docs] def download_data(self, *, session: Optional[requests.Session] = None) -> ContestData: raise NotImplementedError
[docs] def iterate_submissions(self, *, session: Optional[requests.Session] = None) -> Iterator['Submission']: raise NotImplementedError
[docs] @abstractmethod def get_url(self) -> str: raise NotImplementedError
[docs] @abstractmethod def get_service(self) -> Service: raise NotImplementedError
[docs] @classmethod @abstractmethod def from_url(self, s: str) -> Optional['Contest']: pass
[docs]class ProblemData(DownloadedData):
[docs] def url(self) -> str: return self.problem.get_url()
@property @abstractmethod def problem(self) -> 'Problem': raise NotImplementedError @property def contest(self) -> Contest: return self.problem.get_contest() @property def service(self) -> Service: return self.problem.get_service() @property @abstractmethod def name(self) -> str: """ for example of :py:class:`Problem`: - `器物損壊!高橋君` - `AtCoDeerくんと変なじゃんけん / AtCoDeer and Rock-Paper` - `Xor Sum` """ raise NotImplementedError @property def sample_cases(self) -> Optional[List[TestCase]]: raise NotImplementedError
[docs]class Problem(ABC): """ :note: :py:class:`Problem` represents just a URL of a problem, without the data of the problem. :py:class:`Problem` はちょうど問題の URL のみを表現します。キャッシュや内部状態は持ちません。 """
[docs] @abstractmethod def download_sample_cases(self, *, session: Optional[requests.Session] = None) -> List[TestCase]: """ :raises SampleParseError: """ raise NotImplementedError
[docs] def download_system_cases(self, *, session: Optional[requests.Session] = None) -> List[TestCase]: """ :raises NotLoggedInError: """ raise NotImplementedError
[docs] def submit_code(self, code: bytes, language_id: LanguageId, *, filename: Optional[str] = None, session: Optional[requests.Session] = None) -> 'Submission': """ :param code: :arg language_id: :py:class:`LanguageId` :raises NotLoggedInError: :raises SubmissionError: """ raise NotImplementedError
[docs] def get_available_languages(self, *, session: Optional[requests.Session] = None) -> List[Language]: raise NotImplementedError
[docs] @abstractmethod def get_url(self) -> str: raise NotImplementedError
[docs] def get_contest(self) -> Contest: raise NotImplementedError
[docs] @abstractmethod def get_service(self) -> Service: raise NotImplementedError
[docs] def download_data(self, *, session: Optional[requests.Session] = None) -> ProblemData: raise NotImplementedError
def __repr__(self) -> str: return '{}.from_url({})'.format(self.__class__.__name__, repr(self.get_url())) def __eq__(self, other) -> bool: return self.__class__ == other.__class__ and self.get_url() == other.get_url()
[docs] @classmethod @abstractmethod def from_url(self, s: str) -> Optional['Problem']: pass
[docs]class SubmissionData(DownloadedData):
[docs] def url(self) -> str: return self.submission.get_url()
@property @abstractmethod def submission(self) -> 'Submission': raise NotImplementedError @property def problem(self) -> Problem: return self.submission.get_problem() @property def contest(self) -> Contest: return self.submission.get_contest() @property def service(self) -> Service: return self.submission.get_service() @property def source_code(self) -> bytes: raise NotImplementedError @property @abstractmethod def status(self) -> str: raise NotImplementedError
[docs]class Submission(ABC):
[docs] def download_data(self, *, session: Optional[requests.Session] = None) -> SubmissionData: raise NotImplementedError
[docs] @abstractmethod def get_url(self) -> str: raise NotImplementedError
[docs] @abstractmethod def get_problem(self) -> Problem: raise NotImplementedError
[docs] def get_contest(self) -> Contest: return self.get_problem().get_contest()
[docs] @abstractmethod def get_service(self) -> Service: return self.get_problem().get_service()
def __repr__(self) -> str: return '{}.from_url({})'.format(self.__class__.__name__, repr(self.get_url())) def __eq__(self, other) -> bool: return self.__class__ == other.__class__ and self.get_url() == other.get_url()
[docs] @classmethod @abstractmethod def from_url(cls, s: str) -> Optional['Submission']: pass