@@ -72,11 +72,18 @@ def _get_contests(cls, *, session: Optional[requests.Session] = None) -> List[Di
7272 session = session or utils .get_default_session ()
7373 if cls ._contests is None :
7474 cls ._contests = []
75- for url in ('https://yukicoder.me/api/v1/contest/past' , 'https://yukicoder.me/api/v1/contest/future' ):
75+ for tense in ('past' , 'current' , 'future' ):
76+ url = 'https://yukicoder.me/api/v1/contest/{}' .format (tense )
7677 resp = utils .request ('GET' , url , session = session )
7778 cls ._contests .extend (json .loads (resp .content .decode ()))
7879 return cls ._contests
7980
81+ @classmethod
82+ def _get_csrf_token (cls , * , session : requests .Session ) -> str :
83+ url = 'https://yukicoder.me/csrf_token'
84+ resp = utils .request ('GET' , url , session = session )
85+ return resp .content .decode ()
86+
8087
8188class YukicoderContest (onlinejudge .type .Contest ):
8289 """
@@ -93,11 +100,10 @@ def list_problems(self, *, session: Optional[requests.Session] = None) -> Sequen
93100 """
94101
95102 session = session or utils .get_default_session ()
96- for contest in YukicoderService ._get_contests (session = session ):
97- if contest ['Id' ] == self .contest_id :
98- table = {problem ['ProblemId' ]: problem ['No' ] for problem in YukicoderService ._get_problems (session = session )}
99- return [YukicoderProblem (problem_no = table [problem_id ]) for problem_id in contest ['ProblemIdList' ]]
100- raise RuntimeError ('Failed to get the contest information from API: {}' .format (self .get_url ()))
103+ url = 'https://yukicoder.me/api/v1/contest/id/{}' .format (self .contest_id )
104+ resp = utils .request ('GET' , url , session = session )
105+ data = json .loads (resp .content .decode ())
106+ return [YukicoderProblem (problem_id = problem_id ) for problem_id in data ['ProblemIdList' ]]
101107
102108 def get_url (self ) -> str :
103109 return 'https://yukicoder.me/contests/{}' .format (self .contest_id )
@@ -156,7 +162,7 @@ def download_system_cases(self, *, session: Optional[requests.Session] = None) -
156162 url = '{}/testcase.zip' .format (self .get_url ())
157163 resp = utils .request ('GET' , url , session = session )
158164 fmt = 'test_%e/%s'
159- return onlinejudge ._implementation .testcase_zipper .extract_from_zip (resp .content , fmt )
165+ return onlinejudge ._implementation .testcase_zipper .extract_from_zip (resp .content , fmt , ignore_unmatched_samples = True ) # NOTE: yukicoder's test sets sometimes contain garbages. The owner insists that this is an intended behavior, so we need to ignore them.
160166
161167 def _parse_sample_tag (self , tag : bs4 .Tag ) -> Optional [Tuple [str , str ]]:
162168 assert isinstance (tag , bs4 .Tag )
@@ -177,6 +183,8 @@ def submit_code(self, code: bytes, language_id: LanguageId, *, filename: Optiona
177183 :raises NotLoggedInError:
178184 """
179185
186+ # NOTE: An implementation with the official API exists at 492d8d7. This is reverted at 2b7e6f5 because the API ignores cookies and says "提出するにはログインが必要です" at least at that time.
187+
180188 session = session or utils .get_default_session ()
181189 # get
182190 url = self .get_url () + '/submit'
@@ -192,7 +200,7 @@ def submit_code(self, code: bytes, language_id: LanguageId, *, filename: Optiona
192200 form .set ('lang' , language_id )
193201 form .set_file ('file' , filename or 'code' , code )
194202 form .unset ('custom_test' )
195- resp = form .request (session = session )
203+ resp = form .request (headers = { 'referer' : url }, session = session )
196204 resp .raise_for_status ()
197205 # result
198206 if 'submissions' in resp .url :
@@ -208,16 +216,10 @@ def submit_code(self, code: bytes, language_id: LanguageId, *, filename: Optiona
208216
209217 def get_available_languages (self , * , session : Optional [requests .Session ] = None ) -> List [Language ]:
210218 session = session or utils .get_default_session ()
211- # get
212- # We use the problem page since it is available without logging in
213- resp = utils .request ('GET' , self .get_url (), session = session )
214- # parse
215- soup = bs4 .BeautifulSoup (resp .content .decode (resp .encoding ), utils .html_parser )
216- select = soup .find ('select' , id = 'lang' )
217- languages = [] # type: List[Language]
218- for option in select .find_all ('option' ):
219- languages += [Language (option .attrs ['value' ], ' ' .join (option .string .split ()))]
220- return languages
219+ url = 'https://yukicoder.me/api/v1/languages'
220+ resp = utils .request ('GET' , url , session = session )
221+ data = json .loads (resp .content .decode ())
222+ return [Language (language ['Id' ], language ['Name' ] + ' (' + language ['Ver' ] + ')' ) for language in data ]
221223
222224 def get_url (self ) -> str :
223225 if self .problem_no :
0 commit comments