22import zipfile
33import os
44import shutil
5+ import shlex
56import subprocess
67from os .path import isdir , join , exists , dirname
78from django .core .files .storage import FileSystemStorage
@@ -52,9 +53,8 @@ def make_file_executable(file_path):
5253def run_command_with_timeout (command : list , timeout : float ) -> int :
5354 try :
5455 # Use the 'sudo' command to execute the given command with elevated privileges.
55- result = subprocess .run (["/usr/bin/python3" ] + command , capture_output = False , text = True , check = True , timeout = timeout )
56- print ("Command output:" )
57- print (result .stdout )
56+ result = subprocess .run (["/usr/bin/python3" ] + command , capture_output = True , text = True , check = True , timeout = timeout )
57+ # print("Command output:\n", result.stdout)
5858 return TestResult .Succeed
5959 except subprocess .TimeoutExpired :
6060 print (f'program timeout after { timeout } seconds' )
@@ -180,13 +180,55 @@ def __init__(self, submission: Submission):
180180 print (f"substitude { codepath } with user implemented" )
181181 wfp .write (filecontent )
182182
183+ def remove_all_logs (self ):
184+ log_path = join (self .sub_dirpath , 'logs' )
185+ for filename in os .listdir (log_path ):
186+ if filename .startswith ("testcase" ):
187+ file_path = join (log_path , filename )
188+ os .remove (file_path )
189+
190+ def remove_redudant_logs (self , failed_indexes : list ):
191+ log_path = join (self .sub_dirpath , 'logs' )
192+ ignore_files = [f"testcase{ idx } .log" for idx in failed_indexes ]
193+ ignore_files .append ("results.json" )
194+ for filename in os .listdir (log_path ):
195+ if filename in ignore_files :
196+ continue
197+ file_path = join (log_path , filename )
198+ os .remove (file_path )
199+
200+ def trim_logs (self , failed_indexes : list ):
201+ log_path = join (self .sub_dirpath , 'logs' )
202+ max_lines = 1000
203+ for idx in failed_indexes :
204+ file_path = join (log_path , f"testcase{ idx } .log" )
205+ temp_file_path = file_path + ".trimmed"
206+ escaped_file_path = shlex .quote (file_path )
207+ tail_command = f"tail -n { max_lines } { escaped_file_path } > { shlex .quote (temp_file_path )} "
208+ os .system (tail_command )
209+
210+ with open (temp_file_path , 'r' ) as temp_file :
211+ content = temp_file .read ()
212+
213+ if os .path .getsize (file_path ) > os .path .getsize (temp_file_path ):
214+ with open (temp_file_path , 'w' ) as temp_file :
215+ temp_file .write ("log file is too long, only show the last 1000 lines:\n " )
216+ temp_file .write (content )
217+
218+ os .remove (file_path )
219+ os .rename (temp_file_path , file_path )
220+
221+
183222 def judge (self ) -> bool :
184223 # return True if grade is 100
185224 tester_path = join (self .sub_dirpath , TESTER_NAME )
186225 if not exists (tester_path ):
187226 raise Exception ("running submission: tester {} not exists" .format (tester_path ))
188227 print (f"running { tester_path } " )
189228 res = run_command_with_timeout ([tester_path , "--log" , "--json" ], self .sub .problem .timeout )
229+
230+ if res != TestResult .Succeed :
231+ self .remove_all_logs ()
190232 if res == TestResult .Timeout :
191233 self .sub .result = JudgeStatus .PROGRAM_TIMEOUT
192234 self .sub .save ()
@@ -198,39 +240,51 @@ def judge(self) -> bool:
198240 if not exists (log_path ):
199241 raise Exception (f"logs path { log_path } not exists" )
200242 failed_info = []
243+ failed_indexes : list
244+
201245 with open (join (log_path , "results.json" )) as fp :
202246 res = json .load (fp )
203247 grade = res ['grade' ]
204248 self .sub .grade = res ['grade' ]
205- for idx in res ['failed' ]:
206- log_fp = open (join (log_path , f"testcase{ idx } .log" ), "r" )
207- testcase_fp = open (join (self .sub_dirpath , TESTCASE_NAME ), "r" )
208- testcase_json_data = json .load (testcase_fp )
209- # notice the index here, logical index starts from 1
210- if "config" in testcase_json_data :
211- # global config
212- config = testcase_json_data ["config" ]
213- else :
214- cur_data = testcase_json_data ["testcases" ][idx - 1 ]
215- if "config" in cur_data :
216- # specific config for testcase
217- config = cur_data ["config" ]
249+
250+ if grade == 0 or grade == 100 :
251+ self .remove_all_logs ()
252+ else :
253+ # clean and trim
254+ failed_indexes = res ['failed' ]
255+ self .remove_redudant_logs (failed_indexes )
256+ self .trim_logs (failed_indexes )
257+
258+ for idx in res ['failed' ]:
259+ log_fp = open (join (log_path , f"testcase{ idx } .log" ), "r" )
260+ testcase_fp = open (join (self .sub_dirpath , TESTCASE_NAME ), "r" )
261+ testcase_json_data = json .load (testcase_fp )
262+ # notice the index here, logical index starts from 1
263+ if "config" in testcase_json_data :
264+ # global config
265+ config = testcase_json_data ["config" ]
218266 else :
219- # no config
220- config = None
221- displayed_test = {
222- "input" : testcase_json_data ["testcases" ][idx - 1 ]["input" ],
223- "expected_output" :testcase_json_data ["testcases" ][idx - 1 ]["output" ],
224- }
225- # config, "testcase"
226- failed_info .append ({
227- "config" : config ,
228- "testcase_index" : idx ,
229- "testcase" : displayed_test ,
230- "log" : log_fp .read ()
231- })
232- log_fp .close ()
233- testcase_fp .close ()
267+ cur_data = testcase_json_data ["testcases" ][idx - 1 ]
268+ if "config" in cur_data :
269+ # specific config for testcase
270+ config = cur_data ["config" ]
271+ else :
272+ # no config
273+ config = None
274+ displayed_test = {
275+ "input" : testcase_json_data ["testcases" ][idx - 1 ]["input" ],
276+ "expected_output" :testcase_json_data ["testcases" ][idx - 1 ]["output" ],
277+ }
278+ # config, "testcase"
279+ failed_info .append ({
280+ "config" : config ,
281+ "testcase_index" : idx ,
282+ "testcase" : displayed_test ,
283+ "log" : log_fp .read ()
284+ })
285+ log_fp .close ()
286+ testcase_fp .close ()
287+
234288 self .sub .failed_info = failed_info
235289 if grade == 0 :
236290 self .sub .result = JudgeStatus .ALL_FAILED
@@ -239,6 +293,7 @@ def judge(self) -> bool:
239293 else :
240294 self .sub .result = JudgeStatus .SOME_PASSED
241295 self .sub .save ()
296+
242297 if grade == 100 :
243298 return True
244299 return False
0 commit comments