2
2
import zipfile
3
3
import os
4
4
import shutil
5
+ import shlex
5
6
import subprocess
6
7
from os .path import isdir , join , exists , dirname
7
8
from django .core .files .storage import FileSystemStorage
@@ -52,9 +53,8 @@ def make_file_executable(file_path):
52
53
def run_command_with_timeout (command : list , timeout : float ) -> int :
53
54
try :
54
55
# 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)
58
58
return TestResult .Succeed
59
59
except subprocess .TimeoutExpired :
60
60
print (f'program timeout after { timeout } seconds' )
@@ -180,13 +180,55 @@ def __init__(self, submission: Submission):
180
180
print (f"substitude { codepath } with user implemented" )
181
181
wfp .write (filecontent )
182
182
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
+
183
222
def judge (self ) -> bool :
184
223
# return True if grade is 100
185
224
tester_path = join (self .sub_dirpath , TESTER_NAME )
186
225
if not exists (tester_path ):
187
226
raise Exception ("running submission: tester {} not exists" .format (tester_path ))
188
227
print (f"running { tester_path } " )
189
228
res = run_command_with_timeout ([tester_path , "--log" , "--json" ], self .sub .problem .timeout )
229
+
230
+ if res != TestResult .Succeed :
231
+ self .remove_all_logs ()
190
232
if res == TestResult .Timeout :
191
233
self .sub .result = JudgeStatus .PROGRAM_TIMEOUT
192
234
self .sub .save ()
@@ -198,39 +240,51 @@ def judge(self) -> bool:
198
240
if not exists (log_path ):
199
241
raise Exception (f"logs path { log_path } not exists" )
200
242
failed_info = []
243
+ failed_indexes : list
244
+
201
245
with open (join (log_path , "results.json" )) as fp :
202
246
res = json .load (fp )
203
247
grade = res ['grade' ]
204
248
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" ]
218
266
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
+
234
288
self .sub .failed_info = failed_info
235
289
if grade == 0 :
236
290
self .sub .result = JudgeStatus .ALL_FAILED
@@ -239,6 +293,7 @@ def judge(self) -> bool:
239
293
else :
240
294
self .sub .result = JudgeStatus .SOME_PASSED
241
295
self .sub .save ()
296
+
242
297
if grade == 100 :
243
298
return True
244
299
return False
0 commit comments