-
Notifications
You must be signed in to change notification settings - Fork 0
/
assistant_tool.py
159 lines (138 loc) · 6.19 KB
/
assistant_tool.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import platform
import re
import subprocess
import codecs
from pathlib import Path, PurePosixPath
import pandas as pd
from datetime import datetime
import logging
import sys
import config
logging.basicConfig(
format='%(asctime)s %(levelname)s: %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
stream=sys.stderr,
level=logging.DEBUG if config.debug else logging.INFO,
)
SHELL = Path(config.shell)
# make sure that the operation system is 'Windows' or 'Linux'.
operation_system = platform.system()
def allow_ext(file):
pattern = r'\.(c|cpp|CPP|C)$'
regex = re.search(pattern, file)
if regex is None:
logging.debug(f'file extension not allowed')
return False
logging.debug(f'file extension correct')
return True
def encoding_to_utf8(filepath):
"""
deprecated: only can use on windows...
"""
try:
file = codecs.open(filepath, 'r', 'ansi').read()
except UnicodeDecodeError as error:
pass
else:
codecs.open(filepath, 'w', 'utf-8').write(file)
def specify_shell(command):
return [SHELL.__str__(), '-c', command]
class AssistantTool:
def __init__(self, target_week, in_out, grade, export_path, debug) -> None:
super().__init__()
self.target_week = target_week
self.stdin = in_out['stdin']
self.output_pattern = in_out['output_pattern']
self.CORRECT = grade['correct']
self.WRONG = grade['wrong']
self.COMPILE_FAIL = grade['compile_fail']
self.NOT_PAID = grade['not_paid']
self.export_path = export_path
self.debug = debug
self.traverse_dir = f"./{self.target_week}"
self.compile_output = 'compile_output'
def start_program(self):
logging.info(f'start with: {self.target_week}')
logging.debug(f'start traversing the directory')
answers = []
for directory in Path(self.traverse_dir).iterdir():
for file in Path(directory).iterdir():
logging.debug('===================================================')
logging.debug(f'current file name: {file}')
if not allow_ext(file.name):
continue
if self.debug:
input("Press the Enter key to proceed...")
return_code = self.compiler(directory, file)
if return_code == 1:
row_answer = [directory.name, self.COMPILE_FAIL]
else:
row_answer = self.executor(directory)
logging.debug(f'save the result')
logging.debug(f'row_answer: {row_answer}')
answers.append(row_answer)
logging.info(f'program complete')
return answers
def compiler(self, directory, file):
logging.debug(f'start compiling file')
input_filepath = PurePosixPath(file)
output_filepath = PurePosixPath(directory.joinpath(self.compile_output))
# self.encoding_to_utf8(input_filepath)
compiler_command = 'gcc'
compiler_argument = '-lstdc++ -lm -w'
compiler_argument_big5 = '-finput-charset=big5'
if operation_system == 'Windows':
command = f"{compiler_command} '{input_filepath}' -o {output_filepath} {compiler_argument}"
result = subprocess.run(specify_shell(command), shell=True, capture_output=True)
else: # Linux
command = f"{compiler_command} '{input_filepath}' -o {output_filepath} {compiler_argument} {compiler_argument_big5}"
result = subprocess.run(command, shell=True, capture_output=True, executable=SHELL.__str__())
if result.returncode == 0:
logging.debug(f'Compiled successfully')
return result.returncode
logging.debug(f'compilation failed, Try compiling with big5')
if operation_system == 'Windows':
command = f"{compiler_command} '{input_filepath}' -o {output_filepath} {compiler_argument} {compiler_argument_big5}"
result = subprocess.run(specify_shell(command), shell=True, capture_output=True)
else: # Linux
command = f"{compiler_command} '{input_filepath}' -o {output_filepath} {compiler_argument}"
result = subprocess.run(command, shell=True, capture_output=True, executable=SHELL.__str__())
if result.returncode == 0:
logging.debug(f'Compiled successfully')
return result.returncode
logging.debug(f'compilation fails twice, return failure result')
return result.returncode
def executor(self, directory):
logging.debug(f'start file execution')
executable_file = PurePosixPath(directory.joinpath(self.compile_output))
command = f"echo '{' '.join(self.stdin)}' | {executable_file}"
try:
if operation_system == 'Windows':
stdout = subprocess.check_output(specify_shell(command), shell=True, text=True, stderr=subprocess.STDOUT)
else: # Linux
stdout = subprocess.check_output(command, shell=True, text=True, encoding='unicode_escape',stderr=subprocess.STDOUT, executable=SHELL.__str__())
except subprocess.CalledProcessError as error:
logging.debug(f'execution failed, maybe compilation failed')
return [directory.name, self.COMPILE_FAIL]
logging.debug(f'execution succeed')
logging.debug(f'start checking answers')
answer = self.check_answer(stdout)
logging.debug(f'check the answer complete')
logging.debug(f'execute the file complete')
return [directory.name, answer]
def check_answer(self, stdout):
for or_pattern in self.output_pattern:
flag = True
for and_pattern in or_pattern:
regex = re.search(and_pattern, stdout)
if regex is None:
flag = False
if flag:
return self.CORRECT
return self.WRONG
def export_to_csv(self, answers):
csv_filename = f"{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}-{self.target_week}.csv"
df = pd.DataFrame(answers)
directory = Path(self.export_path)
directory.mkdir(parents=True, exist_ok=True)
df.to_csv(directory.joinpath(csv_filename), encoding='utf-8')